├── .gitignore
├── .travis.yml
├── LICENSE
├── NEWS
├── README.md
├── doc
├── index.html
├── nibbles-doc.txt
└── style.css
├── float.lisp
├── macro-utils.lisp
├── nibbles.asd
├── package.lisp
├── sbcl-opt
├── fndb.lisp
├── nib-tran.lisp
├── x86-64-vm.lisp
└── x86-vm.lisp
├── streams.lisp
├── tests.lisp
├── types.lisp
└── vectors.lisp
/.gitignore:
--------------------------------------------------------------------------------
1 | *.fasl
2 | *.FASL
3 | *.ufasl
4 | *.ufsl
5 | *.dx32fsl
6 | *.dx64fsl
7 | *.pfsl
8 | *.dfsl
9 | *.p64fsl
10 | *.d64fsl
11 | *.lx32fsl
12 | *.lx64fsl
13 | *.fx32fsl
14 | *.fx64fsl
15 | *.fas
16 | *.lib
17 |
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os: linux
2 | dist: focal
3 | language: generic
4 |
5 | env:
6 | jobs:
7 | - LISP=sbcl
8 | - LISP=ccl
9 | - LISP=clisp
10 | - LISP=ecl
11 | - LISP=abcl
12 | # - LISP=allegro
13 |
14 | matrix:
15 | fast_finish: true
16 | allow_failures:
17 | - env: LISP=allegro
18 | - env: LISP=clisp
19 |
20 | install:
21 | # Install cl-travis
22 | - curl -L https://raw.githubusercontent.com/lispci/cl-travis/master/install.sh | bash
23 |
24 | script:
25 | - cl -e "(print (lisp-implementation-version))(terpri)
26 | (ql:quickload :nibbles/tests :verbose t)
27 | (uiop:quit (if (rt:do-tests) 0 1))"
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010, Nathan Froyd
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holders nor the names of
17 | contributors to this software may be used to endorse or promote
18 | products derived from this software without specific prior written
19 | permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
22 | IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
25 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 |
--------------------------------------------------------------------------------
/NEWS:
--------------------------------------------------------------------------------
1 | hey emacs, show me an -*- mode: outline -*-
2 |
3 | * Version 0.13, released 2017-03-07
4 |
5 | ** bug fixes
6 |
7 | Fixed problems reading elements into lists.
8 |
9 | Fixed DEFKNOWN issues on newer SBCL versions. (Thanks to Kenan Bölükbaşı.)
10 |
11 | ** new features
12 |
13 | Float accessors are now supported on ABCL. (Thanks to Robert Brown.)
14 |
15 | * Version 0.12, released 2014-10-08
16 |
17 | ** bug fixes
18 |
19 | Better support for Allegro CL modern mode. (Thanks to Markus Flambard.)
20 |
21 | More correct code generation for x86-64 SBCL.
22 |
23 | ** new features
24 |
25 | Float accessors are now MAYBE-INLINE on SBCL. (Thanks to Jan Moringen.)
26 |
27 | * Version 0.11, released 2013-01-14
28 |
29 | ** bug fixes
30 |
31 | IEEE-DOUBLE-REF/* now works correctly on CCL.
32 |
33 | IEEE-SINGLE-REF/* now works correctly on Allegro. (Thanks to Richard
34 | Billington for the bug report.)
35 |
36 | ** new features
37 |
38 | MAKE-OCTET-VECTOR and OCTET-VECTOR convenience functions have been
39 | added, along with the OCTET, OCTET-VECTOR, and SIMPLE-OCTET-VECTOR
40 | types. (Thanks to Jan Moringen.)
41 |
42 | Stream readers and writers for floats have been added, analogous to the
43 | existing functions for integers. These functionsn are only supported on
44 | implementations which support the array accessors (SBCL, CCL, CMUCL for
45 | double-floats; all those including Lispworks and Allegro for
46 | single-floats).
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction #
2 |
3 | When dealing with network protocols and file formats, it's common to
4 | have to read or write 16-, 32-, or 64-bit datatypes in signed or
5 | unsigned flavors. Common Lisp sort of supports this by specifying
6 | `:element-type` for streams, but that facility is underspecified and
7 | there's nothing similar for read/write from octet vectors. What most
8 | people wind up doing is rolling their own small facility for their
9 | particular needs and calling it a day.
10 |
11 | This library attempts to be comprehensive and centralize such
12 | facilities. Functions to read 16-, 32-, and 64-bit quantities from
13 | octet vectors in signed or unsigned flavors are provided; these
14 | functions are also `SETF`able. Since it's sometimes desirable to
15 | read/write directly from streams, functions for doing so are also
16 | provided. Reading/writing IEEE singles/doubles (i.e. `single-float`
17 | and `double-float`) is also supported, with limitations on platforms
18 | that do not fully implement IEEE floating point.
19 |
20 | In addition to centralizing such facilities, NIBBLES also aspires to
21 | become a place where compiler optimizations can be written once and used
22 | everywhere. The intention is that (eventually):
23 |
24 | ``` common-lisp
25 | (nibbles:sb32ref/le vector index)
26 | ```
27 |
28 | will compile (with any necessary safety checks) to a `MOVSX`
29 | instruction on an x86oid processor in SBCL (or other implementations)
30 | if `vector` and `index` are of appropriate types.
31 |
32 | I remember reading a post on comp.lang.lisp that suggested the designers
33 | of Common Lisp ignored the realities of octets and endianness and so
34 | forth. This library is a small step towards remedying that deficiency.
35 |
--------------------------------------------------------------------------------
/doc/index.html:
--------------------------------------------------------------------------------
1 |
3 |
nibbles nibbles nibbles is a library for accessing multibyte integers from
4 | octet arrays and streams. While such accessors are straightforward to
5 | write, nibbles aims to centralize such facilities and also
6 | provide optimizations for them when appropriate.
Installation nibbles can be downloaded at http://www.method-combination.net/lisp/files/nibbles.tar.gz . The latest version is 0.11.
It comes with an ASDF system definition, so (ASDF:OOS
7 | 'ASDF:LOAD-OP :NIBBLES) should be all that you need to get started.
License nibbles is released under a MIT-like license; you can do pretty
8 | much anything you want to with the code except claim that you wrote
9 | it.
Integer array accessors ub16ref/le vector index => value ub32ref/le vector index => value ub64ref/le vector index => value This family of functions accesses an unsigned 16-bit, 32-bit or
10 | 64-bit value stored in little-endian order starting at index in vector . vector must be a (VECTOR (UNSIGNED-BYTE 8)) . These functions are SETFable. For instance:
CL-USER> (nibbles:ub16ref/le (coerce #(42 53) '(vector (unsigned-byte 8))) 0)
11 | 13610
12 | CL-USER> (format nil "~X" *)
13 | "352A" ub16ref/be vector index => value ub32ref/be vector index => value ub64ref/be vector index => value As the above, only the value is accessed in big-endian order. For instance:
CL-USER> (nibbles:ub16ref/be (coerce #(42 53) '(vector (unsigned-byte 8))) 0)
14 | 10805
15 | CL-USER> (format nil "~X" *)
16 | "2A35" sb16ref/le vector index => value sb32ref/le vector index => value sb64ref/le vector index => value sb16ref/be vector index => value sb32ref/be vector index => value sb64ref/be vector index => value As the above, only the value accessed is a signed value. For instance:
CL-USER> (nibbles:sb16ref/be (coerce #(81 92) '(vector (unsigned-byte 8))) 0)
17 | 20828
18 | CL-USER> (nibbles:sb16ref/be (coerce #(129 135) '(vector (unsigned-byte 8))) 0)
19 | -32377
20 | CL-USER> (format nil "~X ~X" ** *)
21 | "515C -7E79"
22 | CL-USER> (nibbles:sb16ref/le (coerce #(81 92) '(vector (unsigned-byte 8))) 0)
23 | 23633
24 | CL-USER> (nibbles:sb16ref/le (coerce #(129 135) '(vector (unsigned-byte 8))) 0)
25 | -30847
26 | CL-USER> (format nil "~X ~X" ** *)
27 | "5C51 -787F" Stream readers read-ub16/le stream => value read-ub32/le stream => value read-ub64/le stream => value This family of functions reads an unsigned 16-bit, 32-bit, or
28 | 64-bit value from stream in little-endian order. stream
29 | must have an element-type of (UNSIGNED-BYTE 8) .
read-ub16/be stream => value read-ub32/be stream => value read-ub64/be stream => value As the above, only the value is read in big-endian order.
read-sb16/le stream => value read-sb32/le stream => value read-sb64/le stream => value read-sb16/be stream => value read-sb32/be stream => value read-sb64/be stream => value As the above, only the value is signed, rather than unsigned.
Stream writers write-ub16/le integer stream => value write-ub32/le integer stream => value write-ub64/le integer stream => value This family of functions writes an unsigned 16-bit, 32-bit, or
30 | 64-bit integer to stream in little-endian order. stream
31 | must have an element-type of (UNSIGNED-BYTE 8) . The value written
32 | is returned.
write-ub16/be integer stream => value write-ub32/be integer stream => value write-ub64/be integer stream => value As the above, only the value is read in big-endian order.
write-sb16/le integer stream => value write-sb32/le integer stream => value write-sb64/le integer stream => value write-sb16/be integer stream => value write-sb32/be integer stream => value write-sb64/be integer stream => value As the above, only the value is signed, rather than unsigned.
--------------------------------------------------------------------------------
/doc/nibbles-doc.txt:
--------------------------------------------------------------------------------
1 | (:author "Nathan Froyd"
2 | :email "froydnj@gmail.com"
3 | :package "nibbles"
4 | :cl-package "NIBBLES"
5 | :version #.(asdf:component-version (asdf:find-system :nibbles))
6 | :homepage "http://www.method-combination.net/lisp/nibbles/"
7 | :download "http://www.method-combination.net/lisp/files/nibbles.tar.gz")
8 |
9 | (:h1 ${package})
10 |
11 | (:p ${package} " is a library for accessing multibyte integers from
12 | octet arrays and streams. While such accessors are straightforward to
13 | write, " ${package} " aims to centralize such facilities and also
14 | provide optimizations for them when appropriate.")
15 |
16 | (:h2 "Installation")
17 |
18 | (:p ${package} " can be downloaded at " (:url ${download} ${download})
19 | ". The latest version is " ${version} ".")
20 |
21 | (:p "It comes with an ASDF system definition, so " `(ASDF:OOS
22 | 'ASDF:LOAD-OP :NIBBLES)` " should be all that you need to get started.")
23 |
24 | (:h2 "License")
25 |
26 | (:p ${package} " is released under a MIT-like license; you can do pretty
27 | much anything you want to with the code except claim that you wrote
28 | it.")
29 |
30 | (:h2 "Integer array accessors")
31 |
32 | (:describe :accessor (nibbles:ub16ref/le value)
33 | (nibbles:ub32ref/le value)
34 | (nibbles:ub64ref/le value))
35 |
36 | (:p "This family of functions accesses an unsigned 16-bit, 32-bit or
37 | 64-bit value stored in little-endian order starting at " 'index' " in "
38 | 'vector' ". " 'vector' " must be a " `(VECTOR (UNSIGNED-BYTE 8))`
39 | ". These functions are SETFable. For instance:")
40 |
41 | (:pre "CL-USER> (nibbles:ub16ref/le (coerce #(42 53) '(vector (unsigned-byte 8))) 0)
42 | 13610
43 | CL-USER> (format nil \"~X\" *)
44 | \"352A\"")
45 |
46 | (:describe :accessor (nibbles:ub16ref/be value)
47 | (nibbles:ub32ref/be value)
48 | (nibbles:ub64ref/be value))
49 |
50 | (:p "As the above, only the value is accessed in big-endian order. For instance:")
51 |
52 | (:pre "CL-USER> (nibbles:ub16ref/be (coerce #(42 53) '(vector (unsigned-byte 8))) 0)
53 | 10805
54 | CL-USER> (format nil \"~X\" *)
55 | \"2A35\"")
56 |
57 | (:describe :accessor (nibbles:sb16ref/le value)
58 | (nibbles:sb32ref/le value)
59 | (nibbles:sb64ref/le value))
60 |
61 | (:describe :accessor (nibbles:sb16ref/be value)
62 | (nibbles:sb32ref/be value)
63 | (nibbles:sb64ref/be value))
64 |
65 | (:p "As the above, only the value accessed is a signed value. For instance:")
66 |
67 | (:pre "CL-USER> (nibbles:sb16ref/be (coerce #(81 92) '(vector (unsigned-byte 8))) 0)
68 | 20828
69 | CL-USER> (nibbles:sb16ref/be (coerce #(129 135) '(vector (unsigned-byte 8))) 0)
70 | -32377
71 | CL-USER> (format nil \"~X ~X\" ** *)
72 | \"515C -7E79\"
73 | CL-USER> (nibbles:sb16ref/le (coerce #(81 92) '(vector (unsigned-byte 8))) 0)
74 | 23633
75 | CL-USER> (nibbles:sb16ref/le (coerce #(129 135) '(vector (unsigned-byte 8))) 0)
76 | -30847
77 | CL-USER> (format nil \"~X ~X\" ** *)
78 | \"5C51 -787F\"")
79 |
80 | (:h2 "Stream readers")
81 |
82 | (:describe :function (nibbles:read-ub16/le value)
83 | (nibbles:read-ub32/le value)
84 | (nibbles:read-ub64/le value))
85 |
86 | (:p "This family of functions reads an unsigned 16-bit, 32-bit, or
87 | 64-bit value from " 'stream' " in little-endian order. " 'stream' "
88 | must have an element-type of " `(UNSIGNED-BYTE 8)` ".")
89 |
90 | (:describe :function (nibbles:read-ub16/be value)
91 | (nibbles:read-ub32/be value)
92 | (nibbles:read-ub64/be value))
93 |
94 | (:p "As the above, only the value is read in big-endian order.")
95 |
96 | (:describe :function (nibbles:read-sb16/le value)
97 | (nibbles:read-sb32/le value)
98 | (nibbles:read-sb64/le value))
99 | (:describe :function (nibbles:read-sb16/be value)
100 | (nibbles:read-sb32/be value)
101 | (nibbles:read-sb64/be value))
102 |
103 | (:p "As the above, only the value is signed, rather than unsigned.")
104 |
105 | (:h2 "Stream writers")
106 |
107 | (:describe :function (nibbles:write-ub16/le value)
108 | (nibbles:write-ub32/le value)
109 | (nibbles:write-ub64/le value))
110 |
111 | (:p "This family of functions writes an unsigned 16-bit, 32-bit, or
112 | 64-bit " 'integer' " to " 'stream' " in little-endian order. " 'stream' "
113 | must have an element-type of " `(UNSIGNED-BYTE 8)` ". The value written
114 | is returned.")
115 |
116 | (:describe :function (nibbles:write-ub16/be value)
117 | (nibbles:write-ub32/be value)
118 | (nibbles:write-ub64/be value))
119 |
120 | (:p "As the above, only the value is read in big-endian order.")
121 |
122 | (:describe :function (nibbles:write-sb16/le value)
123 | (nibbles:write-sb32/le value)
124 | (nibbles:write-sb64/le value))
125 | (:describe :function (nibbles:write-sb16/be value)
126 | (nibbles:write-sb32/be value)
127 | (nibbles:write-sb64/be value))
128 |
129 | (:p "As the above, only the value is signed, rather than unsigned.")
130 |
131 |
--------------------------------------------------------------------------------
/doc/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 1em 5% 1em 5%;
3 | }
4 |
5 | p {
6 | margin-top: 0.5em;
7 | margin-bottom: 0.5em;
8 | }
9 |
10 | pre {
11 | padding: 0;
12 | margin: 0;
13 | }
14 |
15 | h1, h2 {
16 | border-bottom: 2px solid #449977;
17 | }
18 |
19 | h1, h2, h3, h4, h5, h6 {
20 | font-family: sans-serif;
21 | line-height: 1.3;
22 | }
23 |
24 | a:link {
25 | color: #449977;
26 | }
27 |
28 | a:visited {
29 | color: purple;
30 | }
31 |
32 | a {
33 | text-decoration: none;
34 | padding: 1px 2px;
35 | }
36 |
37 | a:hover {
38 | text-decoration: none;
39 | padding: 1px;
40 | border: 1px solid #000000;
41 | }
42 |
43 | .lisp-symbol {
44 | margin-right: 10%;
45 | margin-top: 1.5em;
46 | margin-bottom: 1.5em;
47 | border: 1px solid #449977;
48 | background: #eeeeee;
49 | padding: 0.5em;
50 | }
51 |
52 | .note {
53 | margin-right: 10%;
54 | margin-top: 1.5em;
55 | margin-bottom: 1.5em;
56 | }
57 |
58 | td.content {
59 | padding: 0;
60 | }
61 |
62 | td.title {
63 | font-family: sans-serif;
64 | font-size: 1.1em;
65 | font-weight: bold;
66 | text-align: left;
67 | vertical-align: top;
68 | text-decoration: underline;
69 | padding-right: 0.5em;
70 | margin-top: 0.0em;
71 | margin-bottom: 0.5em;
72 | }
73 |
74 | .note td.content {
75 | padding-left: 0.5em;
76 | border-left: 2px solid #449977;
77 | }
78 |
--------------------------------------------------------------------------------
/float.lisp:
--------------------------------------------------------------------------------
1 | ;;;; float.lisp -- convert between IEEE floating point numbers and bits
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (defun make-single-float (bits)
6 | (let ((exponent-bits (ldb (byte 8 23) bits))
7 | (significand-bits (ldb (byte 23 0) bits)))
8 | (when (= exponent-bits 255)
9 | (error (if (zerop significand-bits)
10 | "floating point infinities are not supported"
11 | "floating point NaNs are not supported")))
12 | #+clisp
13 | (when (and (zerop exponent-bits) (plusp significand-bits))
14 | (error "subnormal floating point numbers are not supported"))
15 | (let ((sign (if (zerop (ldb (byte 1 31) bits)) 1f0 -1f0))
16 | (significand (logior significand-bits (if (zerop exponent-bits) 0 (ash 1 23))))
17 | (exponent (if (zerop exponent-bits) -126 (- exponent-bits 127))))
18 | (* sign (scale-float (float significand 1f0) (- exponent 23))))))
19 |
20 | (defun make-double-float (high low)
21 | (let ((exponent-bits (ldb (byte 11 20) high))
22 | (significand-bits (logior low (ash (ldb (byte 20 0) high) 32))))
23 | (when (= exponent-bits 2047)
24 | (error (if (zerop significand-bits)
25 | "floating point infinities are not supported"
26 | "floating point NaNs are not supported")))
27 | #+clisp
28 | (when (and (zerop exponent-bits) (plusp significand-bits))
29 | (error "subnormal floating point numbers are not supported"))
30 | (let ((sign (if (zerop (ldb (byte 1 31) high)) 1d0 -1d0))
31 | (significand (logior significand-bits (if (zerop exponent-bits) 0 (ash 1 52))))
32 | (exponent (if (zerop exponent-bits) -1022 (- exponent-bits 1023))))
33 | (* sign (scale-float (float significand 1d0) (- exponent 52))))))
34 |
35 | (defun single-float-bits (float)
36 | (multiple-value-bind (significand exponent sign)
37 | (decode-float float)
38 | (let ((sign-bit (if (plusp sign) 0 1))
39 | (exponent-bits (if (zerop significand) 0 (+ exponent 127 -1)))
40 | (significand-bits (floor (* #.(expt 2f0 24) significand))))
41 | (when (<= exponent-bits 0)
42 | (setf significand-bits (ash significand-bits (1- exponent-bits)))
43 | (setf exponent-bits 0))
44 | (logior (ash sign-bit 31) (ash exponent-bits 23) (ldb (byte 23 0) significand-bits)))))
45 |
46 | (defun double-float-bits (float)
47 | (multiple-value-bind (significand exponent sign)
48 | (decode-float float)
49 | (let ((sign-bit (if (plusp sign) 0 1))
50 | (exponent-bits (if (zerop significand) 0 (+ exponent 1023 -1)))
51 | (significand-bits (floor (* #.(expt 2d0 53) significand))))
52 | (when (<= exponent-bits 0)
53 | (setf significand-bits (ash significand-bits (1- exponent-bits)))
54 | (setf exponent-bits 0))
55 | (values
56 | (logior (ash sign-bit 31) (ash exponent-bits 20) (ldb (byte 20 32) significand-bits))
57 | (ldb (byte 32 0) significand-bits)))))
58 |
--------------------------------------------------------------------------------
/macro-utils.lisp:
--------------------------------------------------------------------------------
1 | ;;;; macro-utils.lisp -- functions for compile-time macros
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (defun byte-fun-name (bitsize signedp big-endian-p desc)
6 | (let ((*package* (find-package :nibbles)))
7 | (intern (format nil "~A~D~A/~A"
8 | (symbol-name (if signedp :sb :ub))
9 | bitsize
10 | (symbol-name desc)
11 | (symbol-name (if big-endian-p :be :le))))))
12 |
13 | (defun float-fun-name (float-type big-endian-p desc)
14 | (let ((*package* (find-package :nibbles)))
15 | (intern (format nil "~A-~A-~A/~A"
16 | (symbol-name :ieee)
17 | (symbol-name float-type)
18 | (symbol-name desc)
19 | (symbol-name (if big-endian-p :be :le))))))
20 |
21 | (defun byte-ref-fun-name (bitsize signedp big-endian-p)
22 | (byte-fun-name bitsize signedp big-endian-p :ref))
23 |
24 | (defun float-ref-fun-name (float-type big-endian-p)
25 | (float-fun-name float-type big-endian-p :ref))
26 |
27 | (defun byte-set-fun-name (bitsize signedp big-endian-p)
28 | (byte-fun-name bitsize signedp big-endian-p :set))
29 |
30 | (defun float-set-fun-name (float-type big-endian-p)
31 | (float-fun-name float-type big-endian-p :set))
32 |
33 | (defun stream-ref-fun-name (bitsize readp signedp big-endian-p)
34 | (let ((*package* (find-package :nibbles)))
35 | (intern (format nil "~A-~A~D/~A"
36 | (symbol-name (if readp :read :write))
37 | (symbol-name (if signedp :sb :ub))
38 | bitsize
39 | (symbol-name (if big-endian-p :be :le))))))
40 |
41 | (defun stream-float-ref-fun-name (float-type readp big-endian-p)
42 | (let ((*package* (find-package :nibbles)))
43 | (intern (format nil "~A-~A-~A/~A"
44 | (symbol-name (if readp :read :write))
45 | (symbol-name :ieee)
46 | (symbol-name float-type)
47 | (symbol-name (if big-endian-p :be :le))))))
48 |
49 | (defun stream-seq-fun-name (bitsize readp signedp big-endian-p)
50 | (let ((*package* (find-package :nibbles)))
51 | (intern (format nil "~A-~A~D/~A-~A"
52 | (symbol-name (if readp :read :write))
53 | (symbol-name (if signedp :sb :ub))
54 | bitsize
55 | (symbol-name (if big-endian-p :be :le))
56 | (symbol-name :sequence)))))
57 |
58 | (defun stream-float-seq-fun-name (float-type readp big-endian-p)
59 | (let ((*package* (find-package :nibbles)))
60 | (intern (format nil "~A-~A-~A/~A-~A"
61 | (symbol-name (if readp :read :write))
62 | (symbol-name :ieee)
63 | (symbol-name float-type)
64 | (symbol-name (if big-endian-p :be :le))
65 | (symbol-name :sequence)))))
66 |
67 | (defun stream-into-seq-fun-name (bitsize signedp big-endian-p)
68 | (let ((*package* (find-package :nibbles)))
69 | (intern (format nil "~A-~A~D/~A-~A"
70 | (symbol-name :read)
71 | (symbol-name (if signedp :sb :ub))
72 | bitsize
73 | (symbol-name (if big-endian-p :be :le))
74 | (symbol-name :into-sequence)))))
75 |
76 | (defun stream-float-into-seq-fun-name (float-type big-endian-p)
77 | (let ((*package* (find-package :nibbles)))
78 | (intern (format nil "~A-~A/~A-~A"
79 | (symbol-name :read-ieee)
80 | (symbol-name float-type)
81 | (symbol-name (if big-endian-p :be :le))
82 | (symbol-name :into-sequence)))))
83 |
84 | (defun internalify (s)
85 | (let ((*package* (find-package :nibbles)))
86 | (intern (concatenate 'string "%" (string s)))))
87 |
88 | (defun format-docstring (&rest args)
89 | (loop with docstring = (apply #'format nil args)
90 | for start = 0 then (when pos (1+ pos))
91 | for pos = (and start (position #\Space docstring :start start))
92 | while start
93 | collect (subseq docstring start pos) into words
94 | finally (return (format nil "~{~<~%~1,76:;~A~>~^ ~}"
95 | words))))
96 |
97 | (defun ref-form (vector-name index-name byte-size signedp big-endian-p)
98 | "Return a form that fetches a SIGNEDP BYTE-SIZE value from VECTOR-NAME,
99 | starting at INDEX-NAME. The value is stored in the vector according to
100 | BIG-ENDIAN-P."
101 | (multiple-value-bind (low high increment compare)
102 | (if big-endian-p
103 | (values 0 (1- byte-size) 1 #'>)
104 | (values (1- byte-size) 0 -1 #'<))
105 | (do ((i (+ low increment) (+ i increment))
106 | (shift (* (- byte-size 2) 8) (- shift 8))
107 | (forms nil))
108 | ((funcall compare i high)
109 | `(let* ((high-byte (aref , vector-name
110 | (+ ,index-name ,low)))
111 | ;; Would be great if we could just sign-extend along
112 | ;; with the load, but this is as good as it gets in
113 | ;; portable Common Lisp.
114 | (signed-high ,(if signedp
115 | `(if (logbitp 7 high-byte)
116 | (- high-byte 256)
117 | high-byte)
118 | 'high-byte))
119 | (shifted-into-place
120 | (ash signed-high ,(* (1- byte-size) 8))))
121 | (declare (type (unsigned-byte 8) high-byte))
122 | (declare (type (,(if signedp 'signed-byte 'unsigned-byte) 8)
123 | signed-high))
124 | (logior shifted-into-place ,@(nreverse forms))))
125 | (push `(ash (aref ,vector-name
126 | (+ ,index-name ,i))
127 | ,shift)
128 | forms))))
129 | (defun set-form (vector-name index-name value-name byte-size big-endian-p)
130 | "Return a form that stores a BYTE-SIZE VALUE-NAME into VECTOR-NAME,
131 | starting at INDEX-NAME. The value is stored in the vector according to
132 | BIG-ENDIAN-P. The form returns VALUE-NAME."
133 | `(progn
134 | ,@(loop for i from 1 to byte-size
135 | collect (let ((offset (if big-endian-p
136 | (- byte-size i)
137 | (1- i))))
138 | `(setf (aref ,vector-name
139 | (+ ,index-name ,offset))
140 | (ldb (byte 8 ,(* 8 (1- i))) ,value-name))))
141 | ,value-name))
142 |
--------------------------------------------------------------------------------
/nibbles.asd:
--------------------------------------------------------------------------------
1 | ; -*- mode: lisp -*-
2 |
3 | (cl:defpackage :nibbles-system
4 | (:use :cl))
5 |
6 | (cl:in-package :nibbles-system)
7 |
8 | (defclass nibbles-source-file (asdf:cl-source-file) ())
9 | (defclass txt-file (asdf:doc-file) ((type :initform "txt")))
10 | (defclass css-file (asdf:doc-file) ((type :initform "css")))
11 |
12 | (defmethod asdf:perform :around ((op asdf:compile-op) (c nibbles-source-file))
13 | (let ((*print-base* 10) ; INTERN'ing FORMAT'd symbols
14 | (*print-case* :upcase)
15 | #+sbcl (sb-ext:*inline-expansion-limit* (max sb-ext:*inline-expansion-limit* 1000))
16 | #+cmu (ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000)))
17 | (call-next-method)))
18 |
19 | (defmethod asdf:perform :around ((op asdf:load-op) (c nibbles-source-file))
20 | (call-next-method))
21 |
22 | (asdf:defsystem "nibbles"
23 | :version "0.17"
24 | :author "Nathan Froyd "
25 | :maintainer "Sharp Lispers "
26 | :description "A library for accessing octet-addressed blocks of data in big- and little-endian orders"
27 | :license "BSD-style (http://opensource.org/licenses/BSD-3-Clause)"
28 | :default-component-class nibbles-source-file
29 | :components ((:static-file "README.md")
30 | (:static-file "LICENSE")
31 | (:static-file "NEWS")
32 | (:file "package")
33 | ;; TODO: Add ecl when ECL version 23.9.9 or later is generally available.
34 | ;; If you change this expression, change the one in the test system definition.
35 | #-(or abcl allegro ccl clasp clisp cmu lispworks mezzano sbcl)
36 | (:file "float" :depends-on ("package"))
37 | (:file "types" :depends-on ("package"))
38 | (:file "macro-utils" :depends-on ("package"))
39 | (:file "vectors" :depends-on ("types" "macro-utils"))
40 | (:file "streams" :depends-on ("vectors"))
41 | (:module "doc"
42 | :components ((:html-file "index")
43 | (:txt-file "nibbles-doc")
44 | (:css-file "style")))
45 | (:module "sbcl-opt"
46 | :if-feature :sbcl
47 | :depends-on ("package" "macro-utils")
48 | :components ((:file "fndb")
49 | (:file "nib-tran" :depends-on ("fndb"))
50 | (:file "x86-vm" :if-feature :x86 :depends-on ("fndb"))
51 | (:file "x86-64-vm" :if-feature :x86-64 :depends-on ("fndb")))))
52 | :in-order-to ((asdf:test-op (asdf:test-op "nibbles/tests")))
53 | :perform (asdf:prepare-op (component operation)
54 | ;; nibbles uses SBCL's assembler on x86 and x86-64. The
55 | ;; interface of the assembler with SBCL version 1.4.10.
56 | #+(and sbcl (or x86 x86-64)) (sb-ext:assert-version->= 1 4 10)))
57 |
58 | (asdf:defsystem "nibbles/tests"
59 | :depends-on ("nibbles" "rt")
60 | :version "0.2"
61 | :author "Nathan Froyd "
62 | :maintainer "Sharp Lispers "
63 | :components (;; If you change this expression, change the one in the nibbles system definition.
64 | #+(or abcl allegro ccl clasp clisp cmu lispworks mezzano sbcl)
65 | (:file "float")
66 | (:file "tests"))
67 | :perform (asdf:test-op (operation component)
68 | (or (uiop:symbol-call '#:rtest '#:do-tests)
69 | (error "TEST-OP failed for NIBBLES-TESTS"))))
70 |
--------------------------------------------------------------------------------
/package.lisp:
--------------------------------------------------------------------------------
1 | (cl:defpackage :nibbles
2 | (:use :cl)
3 | ;; Basic types and constructors.
4 | (:export #:octet #:index
5 | #:octet-vector #:simple-octet-vector
6 | #:make-octet-vector)
7 | ;; Basic octet vector accessors.
8 | (:export #:ub16ref/le #:ub16ref/be #:sb16ref/le #:sb16ref/be
9 | #:ub24ref/le #:ub24ref/be #:sb24ref/le #:sb24ref/be
10 | #:ub32ref/le #:ub32ref/be #:sb32ref/le #:sb32ref/be
11 | #:ub64ref/le #:ub64ref/be #:sb64ref/le #:sb64ref/be)
12 | ;; Stream readers.
13 | (:export #:read-ub16/le #:read-ub16/be #:read-sb16/be #:read-sb16/le
14 | #:read-ub24/le #:read-ub24/be #:read-sb24/be #:read-sb24/le
15 | #:read-ub32/le #:read-ub32/be #:read-sb32/be #:read-sb32/le
16 | #:read-ub64/le #:read-ub64/be #:read-sb64/be #:read-sb64/le)
17 | ;; Stream readers for vectors.
18 | (:export #:read-ub16/le-sequence #:read-ub16/be-sequence
19 | #:read-sb16/le-sequence #:read-sb16/be-sequence
20 | #:read-ub24/le-sequence #:read-ub24/be-sequence
21 | #:read-sb24/le-sequence #:read-sb24/be-sequence
22 | #:read-ub32/le-sequence #:read-ub32/be-sequence
23 | #:read-sb32/le-sequence #:read-sb32/be-sequence
24 | #:read-ub64/le-sequence #:read-ub64/be-sequence
25 | #:read-sb64/le-sequence #:read-sb64/be-sequence)
26 | ;; Non-consing variants akin to READ-SEQUENCE.
27 | (:export #:read-ub16/le-into-sequence #:read-ub16/be-into-sequence
28 | #:read-sb16/le-into-sequence #:read-sb16/be-into-sequence
29 | #:read-ub24/le-into-sequence #:read-ub24/be-into-sequence
30 | #:read-sb24/le-into-sequence #:read-sb24/be-into-sequence
31 | #:read-ub32/le-into-sequence #:read-ub32/be-into-sequence
32 | #:read-sb32/le-into-sequence #:read-sb32/be-into-sequence
33 | #:read-ub64/le-into-sequence #:read-ub64/be-into-sequence
34 | #:read-sb64/le-into-sequence #:read-sb64/be-into-sequence)
35 | ;; Stream writers.
36 | (:export #:write-ub16/le #:write-ub16/be #:write-sb16/be #:write-sb16/le
37 | #:write-ub24/le #:write-ub24/be #:write-sb24/be #:write-sb24/le
38 | #:write-ub32/le #:write-ub32/be #:write-sb32/be #:write-sb32/le
39 | #:write-ub64/le #:write-ub64/be #:write-sb64/be #:write-sb64/le)
40 | ;; Stream writers for vectors.
41 | (:export #:write-ub16/le-sequence #:write-ub16/be-sequence
42 | #:write-sb16/le-sequence #:write-sb16/be-sequence
43 | #:write-ub24/le-sequence #:write-ub24/be-sequence
44 | #:write-sb24/le-sequence #:write-sb24/be-sequence
45 | #:write-ub32/le-sequence #:write-ub32/be-sequence
46 | #:write-sb32/le-sequence #:write-sb32/be-sequence
47 | #:write-ub64/le-sequence #:write-ub64/be-sequence
48 | #:write-sb64/le-sequence #:write-sb64/be-sequence)
49 |
50 | ;; The following floating-point functions are not fully supported on all platforms.
51 |
52 | ;; Floating-point octet vector accessors.
53 | (:export #:ieee-single-ref/be #:ieee-single-ref/le
54 | #:ieee-double-ref/be #:ieee-double-ref/le)
55 | ;; Floating-point stream readers.
56 | (:export #:read-ieee-single/be #:read-ieee-single/le
57 | #:read-ieee-double/be #:read-ieee-double/le)
58 | ;; Stream readers for floating-point sequences.
59 | (:export #:read-ieee-single/be-sequence #:read-ieee-single/le-sequence
60 | #:read-ieee-double/be-sequence #:read-ieee-double/le-sequence)
61 | ;; Non-consing variants akin to READ-SEQUENCE.
62 | (:export #:read-ieee-single/be-into-sequence #:read-ieee-single/le-into-sequence
63 | #:read-ieee-double/be-into-sequence #:read-ieee-double/le-into-sequence)
64 | ;; Stream writers.
65 | (:export #:write-ieee-single/be #:write-ieee-single/le
66 | #:write-ieee-double/be #:write-ieee-double/le)
67 | ;; Stream writers for sequences.
68 | (:export #:write-ieee-single/be-sequence #:write-ieee-single/le-sequence
69 | #:write-ieee-double/be-sequence #:write-ieee-double/le-sequence))
70 |
--------------------------------------------------------------------------------
/sbcl-opt/fndb.lisp:
--------------------------------------------------------------------------------
1 | ;;;; fndb.lisp -- DEFKNOWNish bits for SBCL
2 |
3 | (cl:in-package :nibbles)
4 |
5 | ;;; Efficient array bounds checking
6 | (sb-c:defknown %check-bound
7 | ((simple-array (unsigned-byte 8) (*)) index (and fixnum sb-vm:word)
8 | (member 2 4 3 8 16))
9 | index (sb-c:any) :overwrite-fndb-silently t)
10 |
11 | ;; We DEFKNOWN the exported functions so we can DEFTRANSFORM them.
12 | ;; We DEFKNOWN the %-functions so we can DEFINE-VOP them.
13 |
14 | #.(loop for i from 0 to #-x86-64 #b1011 #+x86-64 #b1111
15 | for bitsize = (ecase (ldb (byte 2 2) i)
16 | (0 16)
17 | (1 24)
18 | (2 32)
19 | (3 64))
20 | for signedp = (logbitp 1 i)
21 | for setterp = (logbitp 0 i)
22 | for byte-fun = (if setterp
23 | #'byte-set-fun-name
24 | #'byte-ref-fun-name)
25 | for big-fun = (funcall byte-fun bitsize signedp t)
26 | for little-fun = (funcall byte-fun bitsize signedp nil)
27 | for internal-big = (internalify big-fun)
28 | for internal-little = (internalify little-fun)
29 | for arg-type = `(,(if signedp
30 | 'signed-byte
31 | 'unsigned-byte)
32 | ,bitsize)
33 | for external-arg-types = `(array index ,@(when setterp
34 | `(,arg-type)))
35 | for internal-arg-types = (subst '(simple-array (unsigned-byte 8)) 'array
36 | external-arg-types)
37 | collect `(sb-c:defknown (,big-fun ,little-fun) ,external-arg-types
38 | ,arg-type (sb-c:any) :overwrite-fndb-silently t) into defknowns
39 | collect `(sb-c:defknown (,internal-big ,internal-little)
40 | ,internal-arg-types
41 | ,arg-type (sb-c:any) :overwrite-fndb-silently t) into defknowns
42 | finally (return `(progn ,@defknowns)))
43 |
--------------------------------------------------------------------------------
/sbcl-opt/nib-tran.lisp:
--------------------------------------------------------------------------------
1 | ;;;; nib-tran.lisp -- DEFTRANSFORMs for SBCL
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (sb-c:deftransform %check-bound ((vector bound offset n-bytes)
6 | ((simple-array (unsigned-byte 8) (*)) index
7 | (and fixnum sb-vm:word)
8 | (member 2 3 4 8 16))
9 | * :node node)
10 | "optimize away bounds check"
11 | ;; cf. sb-c::%check-bound transform
12 | (cond ((sb-c:policy node (= sb-c::insert-array-bounds-checks 0))
13 | 'offset)
14 | ((not (sb-c::constant-lvar-p bound))
15 | (sb-c::give-up-ir1-transform))
16 | (t
17 | (let* ((dim (sb-c::lvar-value bound))
18 | (n-bytes (sb-c::lvar-value n-bytes))
19 | (upper-bound `(integer 0 (,(- dim n-bytes -1)))))
20 | (if (> n-bytes dim)
21 | (sb-c::give-up-ir1-transform)
22 | `(the ,upper-bound offset))))))
23 |
24 | #.(flet ((specialized-includep (bitsize signedp setterp)
25 | (declare (ignorable bitsize signedp setterp))
26 | ;; Bleh. No good way to solve this atm.
27 | ;;
28 | ;; Non-x86. No support.
29 | #-(or x86 x86-64)
30 | nil
31 | ;; x86 and x86-64. Can do everything.
32 | #+(or x86 x86-64)
33 | t)
34 | (generic-transform-form (fun-name arglist n-bytes
35 | setterp signedp big-endian-p)
36 | (let ((offset-type `(integer 0 ,(- array-dimension-limit n-bytes))))
37 | `(sb-c:deftransform ,fun-name ,arglist
38 | `(locally (declare (type ,',offset-type offset))
39 | ,',(if setterp
40 | (set-form 'vector 'offset 'value n-bytes big-endian-p)
41 | (ref-form 'vector 'offset n-bytes signedp big-endian-p)))))))
42 | (loop for i from 0 to #-x86-64 #b1011 #+x86-64 #b1111
43 | for bitsize = (ecase (ldb (byte 2 2) i)
44 | (0 16)
45 | (1 24)
46 | (2 32)
47 | (3 64))
48 | for signedp = (logbitp 1 i)
49 | for setterp = (logbitp 0 i)
50 | for byte-fun = (if setterp
51 | #'byte-set-fun-name
52 | #'byte-ref-fun-name)
53 | for big-fun = (funcall byte-fun bitsize signedp t)
54 | for little-fun = (funcall byte-fun bitsize signedp nil)
55 | for internal-big = (internalify big-fun)
56 | for internal-little = (internalify little-fun)
57 | for n-bytes = (truncate bitsize 8)
58 | for arg-type = `(,(if signedp
59 | 'signed-byte
60 | 'unsigned-byte)
61 | ,bitsize)
62 | for arglist = `(vector offset ,@(when setterp '(value)))
63 | for external-arg-types = `(array index ,@(when setterp
64 | `(,arg-type)))
65 | for internal-arg-types = (subst '(simple-array (unsigned-byte 8)) 'array
66 | external-arg-types)
67 | for transform-arglist = `(,arglist ,internal-arg-types ,arg-type)
68 | for specialized-big-transform
69 | = `(sb-c:deftransform ,big-fun ,transform-arglist
70 | '(,internal-big vector (%check-bound vector (length vector) offset ,n-bytes)
71 | ,@(when setterp '(value))))
72 | for specialized-little-transform
73 | = (subst internal-little internal-big
74 | (subst little-fun big-fun
75 | specialized-big-transform))
76 | ;; Also include inlining versions for when the argument type
77 | ;; is known to be a simple octet vector and we don't have a
78 | ;; native assembly implementation.
79 | for generic-big-transform
80 | = (generic-transform-form big-fun transform-arglist n-bytes
81 | setterp signedp t)
82 | for generic-little-transform
83 | = (generic-transform-form little-fun transform-arglist n-bytes
84 | setterp signedp nil)
85 | if (specialized-includep bitsize signedp setterp)
86 | collect specialized-big-transform into transforms
87 | else if (<= bitsize sb-vm:n-word-bits)
88 | collect generic-big-transform into transforms
89 | if (specialized-includep bitsize signedp setterp)
90 | collect specialized-little-transform into transforms
91 | else if (<= bitsize sb-vm:n-word-bits)
92 | collect generic-little-transform into transforms
93 | finally (return `(progn ,@transforms))))
94 |
--------------------------------------------------------------------------------
/sbcl-opt/x86-64-vm.lisp:
--------------------------------------------------------------------------------
1 | ;;;; x86-64-vm.lisp -- VOP definitions SBCL
2 |
3 | (cl:in-package :sb-vm)
4 |
5 | (define-vop (%check-bound)
6 | (:translate nibbles::%check-bound)
7 | (:policy :fast-safe)
8 | (:args (array :scs (descriptor-reg))
9 | (bound :scs (any-reg))
10 | (index :scs (any-reg)))
11 | (:arg-types simple-array-unsigned-byte-8 positive-fixnum tagged-num
12 | (:constant (member 2 3 4 8 16)))
13 | (:info offset)
14 | (:temporary (:sc any-reg) temp)
15 | (:results (result :scs (any-reg)))
16 | (:result-types positive-fixnum)
17 | (:vop-var vop)
18 | (:generator 5
19 | (let ((error (generate-error-code vop 'invalid-array-index-error
20 | array bound index)))
21 | ;; We want to check the conditions:
22 | ;;
23 | ;; 0 <= INDEX
24 | ;; INDEX < BOUND
25 | ;; 0 <= INDEX + OFFSET
26 | ;; (INDEX + OFFSET) < BOUND
27 | ;;
28 | ;; We can do this naively with two unsigned checks:
29 | ;;
30 | ;; INDEX <_u BOUND
31 | ;; INDEX + OFFSET <_u BOUND
32 | ;;
33 | ;; If INDEX + OFFSET <_u BOUND, though, INDEX must be less than
34 | ;; BOUND. We *do* need to check for 0 <= INDEX, but that has
35 | ;; already been assured by higher-level machinery.
36 | (inst lea temp (ea (fixnumize offset) nil index))
37 | (inst cmp temp bound)
38 | (inst jmp :a error)
39 | (move result index))))
40 |
41 | #.(flet ((frob (bitsize setterp signedp big-endian-p)
42 | (let* ((name (funcall (if setterp
43 | #'nibbles::byte-set-fun-name
44 | #'nibbles::byte-ref-fun-name)
45 | bitsize signedp big-endian-p))
46 | (internal-name (nibbles::internalify name))
47 | (operand-size (ecase bitsize
48 | (16 :word)
49 | (32 :dword)
50 | (64 :qword)))
51 | (ref-mov-insn (ecase bitsize
52 | (16
53 | (if big-endian-p
54 | 'movzx
55 | (if signedp 'movsx 'movzx)))
56 | (32
57 | (if big-endian-p
58 | 'mov
59 | (if signedp 'movsxd 'movzxd)))
60 | (64 'mov)))
61 | (result-sc (if signedp 'signed-reg 'unsigned-reg))
62 | (result-type (if signedp 'signed-num 'unsigned-num)))
63 | (flet ((movx (insn dest source source-size)
64 | (cond ((eq insn 'mov)
65 | `(inst ,insn ,dest ,source))
66 | ;; (movzx (:dword :qword) dest source) is
67 | ;; no longer allowed on SBCL > 2.1.4.134
68 | ;; but older versions already supported
69 | ;; this new spelling.
70 | ((and (member insn '(movzx movzxd))
71 | (eq source-size :dword))
72 | `(inst mov :dword ,dest ,source))
73 | (t
74 | `(inst ,(case insn (movsxd 'movsx) (movzxd 'movzx) (t insn))
75 | '(,source-size :qword) ,dest ,source))))
76 | (swap-tn-inst-form (tn-name)
77 | (if (= bitsize 16)
78 | `(inst rol ,operand-size ,tn-name 8)
79 | ;; The '(bswap :dword r)' notation is only
80 | ;; supported on SBCL > 1.5.9.
81 | (if (ignore-errors (sb-ext:assert-version->= 1 5 9 17) t)
82 | `(inst bswap ,operand-size ,tn-name)
83 | `(inst bswap (sb-vm::reg-in-size ,tn-name ,operand-size))))))
84 | `(define-vop (,name)
85 | (:translate ,internal-name)
86 | (:policy :fast-safe)
87 | (:args (vector :scs (descriptor-reg))
88 | (index :scs (immediate unsigned-reg))
89 | ,@(when setterp
90 | `((value* :scs (,result-sc) :target result))))
91 | (:arg-types simple-array-unsigned-byte-8
92 | positive-fixnum
93 | ,@(when setterp
94 | `(,result-type)))
95 | ,@(when (and setterp big-endian-p)
96 | `((:temporary (:sc unsigned-reg
97 | :from (:load 0)
98 | :to (:result 0)) temp)))
99 | (:results (result :scs (,result-sc)))
100 | (:result-types ,result-type)
101 | (:generator 3
102 | (let* ((base-disp (- (* vector-data-offset n-word-bytes)
103 | other-pointer-lowtag))
104 | (memref (sc-case index
105 | (immediate
106 | (ea (+ (tn-value index) base-disp) vector))
107 | (t
108 | (ea base-disp vector index)))))
109 | ,@(when (and setterp big-endian-p)
110 | `((inst mov temp value*)
111 | ,(swap-tn-inst-form 'temp)))
112 | ,(if setterp
113 | `(inst mov ,operand-size memref ,(if big-endian-p
114 | 'temp
115 | 'value*))
116 | (movx ref-mov-insn 'result 'memref operand-size))
117 | ,@(if setterp
118 | '((move result value*))
119 | (when big-endian-p
120 | `(,(swap-tn-inst-form 'result)
121 | ,(when (and (/= bitsize 64) signedp)
122 | (movx 'movsx 'result 'result operand-size))))))))))))
123 | (loop for i from 0 upto #b10111
124 | for bitsize = (ecase (ldb (byte 2 3) i)
125 | (0 16)
126 | (1 32)
127 | (2 64))
128 | for setterp = (logbitp 2 i)
129 | for signedp = (logbitp 1 i)
130 | for big-endian-p = (logbitp 0 i)
131 | collect (frob bitsize setterp signedp big-endian-p) into forms
132 | finally (return `(progn ,@forms))))
133 |
134 | ;;; 24-bit accessors need to be handled specially.
135 | #.(flet ((frob (setterp signedp big-endian-p)
136 | (let* ((name (funcall (if setterp
137 | #'nibbles::byte-set-fun-name
138 | #'nibbles::byte-ref-fun-name)
139 | 24 signedp big-endian-p))
140 | (internal-name (nibbles::internalify name))
141 | (result-sc (if signedp 'signed-reg 'unsigned-reg))
142 | (result-type (if signedp 'signed-num 'unsigned-num))
143 | (mov-insn (if signedp 'movsx 'movzx)))
144 | `(define-vop (,name)
145 | (:translate ,internal-name)
146 | (:policy :fast-safe)
147 | (:args (vector :scs (descriptor-reg))
148 | (index :scs (immediate unsigned-reg))
149 | ,@(when setterp `((value :scs (,result-sc) :target result))))
150 | (:arg-types simple-array-unsigned-byte-8 positive-fixnum
151 | ,@(when setterp `(,result-type)))
152 | ,@(when setterp
153 | `((:temporary (:sc unsigned-reg
154 | :from (:load 0)
155 | :to (:result 0)) temp)))
156 | (:results (result :scs (,result-sc) :from (:load 0)))
157 | (:result-types ,result-type)
158 | (:generator 3
159 | (let* ((base-disp (- (* vector-data-offset n-word-bytes)
160 | other-pointer-lowtag))
161 | (low-memref
162 | (sc-case index
163 | (immediate
164 | (ea (+ (tn-value index) base-disp) vector))
165 | (t
166 | (ea base-disp vector index))))
167 | (high-memref
168 | (sc-case index
169 | (immediate
170 | (ea (+ (tn-value index) base-disp 2) vector))
171 | (t
172 | (ea (+ base-disp 2) vector index)))))
173 | ,@(cond
174 | ((and (not setterp) (not big-endian-p))
175 | `((inst ,mov-insn '(:byte :qword) result high-memref)
176 | (inst shl result 16)
177 | (inst mov :word result low-memref)))
178 | ((and (not setterp) big-endian-p)
179 | `((inst mov result low-memref)
180 | (inst rol :word result 8)
181 | (inst ,mov-insn '(:word :qword) result result)
182 | (inst shl result 8)
183 | (inst mov :byte result high-memref)))
184 | ((and setterp (not big-endian-p))
185 | '((inst mov temp value)
186 | (inst mov :word low-memref value)
187 | (inst shr temp 16)
188 | (inst mov :byte high-memref temp)
189 | (move result value)))
190 | ((and setterp big-endian-p)
191 | '((inst mov temp value)
192 | ;; TEMP has the bytes 0 High Mid Low
193 | (inst bswap :dword temp)
194 | ;; L M H 0
195 | (inst shr temp 8)
196 | ;; 0 L M H
197 | (inst mov :word low-memref temp)
198 | (inst shr temp 16)
199 | ;; 0 0 0 L
200 | (inst mov :byte high-memref temp)
201 | (move result value))))))))))
202 | (loop for i from 0 upto #b111
203 | for setterp = (logbitp 2 i)
204 | for signedp = (logbitp 1 i)
205 | for big-endian-p = (logbitp 0 i)
206 | collect (frob setterp signedp big-endian-p) into forms
207 | finally (return `(progn ,@forms))))
208 |
--------------------------------------------------------------------------------
/sbcl-opt/x86-vm.lisp:
--------------------------------------------------------------------------------
1 | ;;;; x86-vm.lisp -- VOP definitions for SBCL
2 |
3 | (cl:in-package :sb-vm)
4 |
5 | (define-vop (%check-bound)
6 | (:translate nibbles::%check-bound)
7 | (:policy :fast-safe)
8 | (:args (array :scs (descriptor-reg))
9 | (bound :scs (any-reg))
10 | (index :scs (any-reg)))
11 | (:arg-types simple-array-unsigned-byte-8 positive-fixnum tagged-num
12 | (:constant (member 2 4 8 16)))
13 | (:info offset)
14 | (:temporary (:sc any-reg) temp)
15 | (:results (result :scs (any-reg)))
16 | (:result-types positive-fixnum)
17 | (:vop-var vop)
18 | (:generator 5
19 | (let ((error (generate-error-code vop 'invalid-array-index-error
20 | array bound index)))
21 | ;; We want to check the conditions:
22 | ;;
23 | ;; 0 <= INDEX
24 | ;; INDEX < BOUND
25 | ;; 0 <= INDEX + OFFSET
26 | ;; (INDEX + OFFSET) < BOUND
27 | ;;
28 | ;; We can do this naively with two unsigned checks:
29 | ;;
30 | ;; INDEX <_u BOUND
31 | ;; INDEX + OFFSET <_u BOUND
32 | ;;
33 | ;; If INDEX + OFFSET <_u BOUND, though, INDEX must be less than
34 | ;; BOUND. We *do* need to check for 0 <= INDEX, but that has
35 | ;; already been assured by higher-level machinery.
36 | (inst lea temp (make-ea :dword :index index :disp (fixnumize offset)))
37 | (inst cmp temp bound)
38 | (inst jmp :a error)
39 | (move result index))))
40 |
41 | #.(flet ((frob (setterp signedp big-endian-p)
42 | (let* ((name (funcall (if setterp
43 | #'nibbles::byte-set-fun-name
44 | #'nibbles::byte-ref-fun-name)
45 | 16 signedp big-endian-p))
46 | (internal-name (nibbles::internalify name))
47 | (result-sc (if signedp 'signed-reg 'unsigned-reg))
48 | (result-type (if signedp 'signed-num 'unsigned-num)))
49 | `(define-vop (,name)
50 | (:translate ,internal-name)
51 | (:policy :fast-safe)
52 | (:args (vector :scs (descriptor-reg))
53 | (index :scs (immediate unsigned-reg))
54 | ,@(when setterp
55 | `((value :scs (,result-sc) :target result))))
56 | (:arg-types simple-array-unsigned-byte-8
57 | positive-fixnum
58 | ,@(when setterp
59 | `(,result-type)))
60 | ,@(when (or setterp big-endian-p)
61 | `((:temporary (:sc unsigned-reg :offset eax-offset
62 | :from ,(if setterp
63 | '(:load 0)
64 | '(:argument 2))
65 | :to (:result 0)) eax)))
66 | (:results (result :scs (,result-sc)))
67 | (:result-types ,result-type)
68 | (:generator 3
69 | (let* ((base-disp (- (* vector-data-offset n-word-bytes)
70 | other-pointer-lowtag))
71 | (memref (sc-case index
72 | (immediate
73 | (make-ea :word :base vector
74 | :disp (+ (tn-value index) base-disp)))
75 | (t
76 | (make-ea :word :base vector
77 | :index index
78 | :disp base-disp)))))
79 | ,(when setterp
80 | '(move eax value))
81 | ,(when (and setterp big-endian-p)
82 | '(inst rol ax-tn 8))
83 | ,(if setterp
84 | '(inst mov memref ax-tn)
85 | `(inst ,(if big-endian-p
86 | 'mov
87 | (if signedp
88 | 'movsx
89 | 'movzx))
90 | ,(if big-endian-p
91 | 'ax-tn
92 | 'result)
93 | memref))
94 | ,@(if setterp
95 | '((move result value))
96 | (when big-endian-p
97 | `(eax ; hack so that it looks used
98 | (inst rol ax-tn 8)
99 | (inst ,(if signedp 'movsx 'movzx)
100 | result ax-tn))))))))))
101 | (loop for i from 0 upto #b111
102 | for setterp = (logbitp 2 i)
103 | for signedp = (logbitp 1 i)
104 | for big-endian-p = (logbitp 0 i)
105 | collect (frob setterp signedp big-endian-p) into forms
106 | finally (return `(progn ,@forms))))
107 |
108 | #.(flet ((frob (setterp signedp big-endian-p)
109 | (let* ((name (funcall (if setterp
110 | #'nibbles::byte-set-fun-name
111 | #'nibbles::byte-ref-fun-name)
112 | 32 signedp big-endian-p))
113 | (internal-name (nibbles::internalify name))
114 | (result-sc (if signedp 'signed-reg 'unsigned-reg))
115 | (result-type (if signedp 'signed-num 'unsigned-num)))
116 | `(define-vop (,name)
117 | (:translate ,internal-name)
118 | (:policy :fast-safe)
119 | (:args (vector :scs (descriptor-reg))
120 | (index :scs (immediate unsigned-reg))
121 | ,@(when setterp
122 | `((value :scs (,result-sc) :target result))))
123 | (:arg-types simple-array-unsigned-byte-8
124 | positive-fixnum
125 | ,@(when setterp
126 | `(,result-type)))
127 | ,@(when (and setterp big-endian-p)
128 | `((:temporary (:sc unsigned-reg
129 | :from (:load 0)
130 | :to (:result 0)) temp)))
131 | (:results (result :scs (,result-sc)))
132 | (:result-types ,result-type)
133 | (:generator 3
134 | (let* ((base-disp (- (* vector-data-offset n-word-bytes)
135 | other-pointer-lowtag))
136 | (memref (sc-case index
137 | (immediate
138 | (make-ea :dword :base vector
139 | :disp (+ (tn-value index) base-disp)))
140 | (t
141 | (make-ea :dword :base vector :index index
142 | :disp base-disp)))))
143 | ,@(when (and setterp big-endian-p)
144 | `((inst mov temp value)
145 | (inst bswap temp)))
146 | ,(if setterp
147 | `(inst mov memref ,(if big-endian-p
148 | 'temp
149 | 'value))
150 | '(inst mov result memref))
151 | ,(if setterp
152 | '(move result value)
153 | (when big-endian-p
154 | '(inst bswap result)))))))))
155 | (loop for i from 0 upto #b111
156 | for setterp = (logbitp 2 i)
157 | for signedp = (logbitp 1 i)
158 | for big-endian-p = (logbitp 0 i)
159 | collect (frob setterp signedp big-endian-p) into forms
160 | finally (return `(progn ,@forms))))
161 |
--------------------------------------------------------------------------------
/streams.lisp:
--------------------------------------------------------------------------------
1 | ;;;; streams.lisp -- reading/writing signed/unsigned bytes to streams
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (defun read-n-bytes-into (stream n-bytes v)
6 | (declare (type (integer 2 8) n-bytes))
7 | (dotimes (i n-bytes v)
8 | ;; READ-SEQUENCE would likely be more efficient here, but it does
9 | ;; not have the semantics we want--in particular, the blocking
10 | ;; semantics of READ-SEQUENCE are potentially bad. It's not clear
11 | ;; that READ-BYTE is any better here, though...
12 | (setf (aref v i) (read-byte stream))))
13 |
14 | (declaim (inline read-byte* write-byte*))
15 | (defun read-byte* (stream n-bytes reffer)
16 | (declare (type (integer 2 8) n-bytes))
17 | (let ((v (make-octet-vector n-bytes)))
18 | (declare (dynamic-extent v))
19 | (read-n-bytes-into stream n-bytes v)
20 | (funcall reffer v 0)))
21 |
22 | (defun write-byte* (integer stream n-bytes setter)
23 | (declare (type (integer 2 8) n-bytes))
24 | (let ((v (make-octet-vector n-bytes)))
25 | (declare (dynamic-extent v))
26 | (funcall setter v 0 integer)
27 | (write-sequence v stream)
28 | integer))
29 |
30 | (declaim (inline read-into-vector*))
31 | (defun read-into-vector* (stream vector start end n-bytes reffer)
32 | (declare (type (integer 2 8) n-bytes)
33 | (type function reffer))
34 | (let ((v (make-octet-vector n-bytes)))
35 | (declare (dynamic-extent v))
36 | (loop for i from start below end
37 | do (read-n-bytes-into stream n-bytes v)
38 | (setf (aref vector i) (funcall reffer v 0))
39 | finally (return vector))))
40 |
41 | (defun read-into-list* (stream list start end n-bytes reffer)
42 | (declare (type (integer 2 8) n-bytes)
43 | (type function reffer))
44 | (do ((end (or end (length list)))
45 | (v (make-octet-vector n-bytes))
46 | (rem (nthcdr start list) (rest rem))
47 | (i start (1+ i)))
48 | ((or (endp rem) (>= i end)) list)
49 | (declare (dynamic-extent v))
50 | (read-n-bytes-into stream n-bytes v)
51 | (setf (first rem) (funcall reffer v 0))))
52 |
53 | (declaim (inline read-fresh-sequence))
54 | (defun read-fresh-sequence (result-type stream count
55 | element-type n-bytes reffer)
56 | (ecase result-type
57 | (list
58 | (let ((list (make-list count)))
59 | (read-into-list* stream list 0 count n-bytes reffer)))
60 | (vector
61 | (let ((vector (make-array count :element-type element-type)))
62 | (read-into-vector* stream vector 0 count n-bytes reffer)))))
63 |
64 | (defun write-sequence-with-writer (seq stream start end writer)
65 | (declare (type function writer))
66 | (etypecase seq
67 | (list
68 | (mapc (lambda (e) (funcall writer e stream))
69 | (subseq seq start end))
70 | seq)
71 | (vector
72 | (loop with end = (or end (length seq))
73 | for i from start below end
74 | do (funcall writer (aref seq i) stream)
75 | finally (return seq)))))
76 |
77 | (defun read-into-sequence (seq stream start end n-bytes reffer)
78 | (declare (type (integer 2 8) n-bytes))
79 | (etypecase seq
80 | (list
81 | (read-into-list* stream seq start end n-bytes reffer))
82 | (vector
83 | (let ((end (or end (length seq))))
84 | (read-into-vector* stream seq start end n-bytes reffer)))))
85 |
86 | #.(loop for i from 0 upto #b11111
87 | for bitsize = (ecase (ldb (byte 2 3) i)
88 | (0 16)
89 | (3 24)
90 | (1 32)
91 | (2 64))
92 | for readp = (logbitp 2 i)
93 | for signedp = (logbitp 1 i)
94 | for big-endian-p = (logbitp 0 i)
95 | for name = (stream-ref-fun-name bitsize readp signedp big-endian-p)
96 | for n-bytes = (truncate bitsize 8)
97 | for byte-fun = (if readp
98 | (byte-ref-fun-name bitsize signedp big-endian-p)
99 | (byte-set-fun-name bitsize signedp big-endian-p))
100 | for byte-arglist = (if readp '(stream) '(integer stream))
101 | for subfun = (if readp 'read-byte* 'write-byte*)
102 | for element-type = `(,(if signedp 'signed-byte 'unsigned-byte) ,bitsize)
103 | collect `(progn
104 | ,@(when readp
105 | `((declaim (ftype (function (t) (values ,element-type &optional)) ,name))))
106 | (defun ,name ,byte-arglist
107 | (,subfun ,@byte-arglist ,n-bytes #',byte-fun))) into forms
108 | if readp
109 | collect `(defun ,(stream-seq-fun-name bitsize t signedp big-endian-p)
110 | (result-type stream count)
111 | ,(format-docstring "Return a sequence of type RESULT-TYPE, containing COUNT elements read from STREAM. Each element is a ~D-bit ~:[un~;~]signed integer read in ~:[little~;big~]-endian order. RESULT-TYPE must be either CL:VECTOR or CL:LIST. STREAM must have an element type of (UNSIGNED-BYTE 8)."
112 | bitsize signedp big-endian-p)
113 | (read-fresh-sequence result-type stream count
114 | ',element-type ,n-bytes #',byte-fun)) into forms
115 | else
116 | collect `(defun ,(stream-seq-fun-name bitsize nil signedp big-endian-p)
117 | (seq stream &key (start 0) end)
118 | ,(format-docstring "Write elements from SEQ between START and END as ~D-bit ~:[un~;~]signed integers in ~:[little~;big~]-endian order to STREAM. SEQ may be either a vector or a list. STREAM must have an element type of (UNSIGNED-BYTE 8)."
119 | bitsize signedp big-endian-p)
120 | (write-sequence-with-writer seq stream start end #',name)) into forms
121 | if readp
122 | collect `(defun ,(stream-into-seq-fun-name bitsize signedp big-endian-p)
123 | (seq stream &key (start 0) end)
124 | ,(format-docstring "Destructively modify SEQ by replacing the elements of SEQ between START and END with elements read from STREAM. Each element is a ~D-bit ~:[un~;~]signed integer read in ~:[little~;big~]-endian order. SEQ may be either a vector or a list. STREAM must have an element type of (UNSIGNED-BYTE 8)."
125 | bitsize signedp big-endian-p)
126 | (read-into-sequence seq stream start end ,n-bytes #',byte-fun)) into forms
127 | finally (return `(progn ,@forms)))
128 |
129 | #.(loop for i from 0 upto #b111
130 | for float-type = (if (logbitp 2 i) 'double 'single)
131 | for readp = (logbitp 1 i)
132 | for big-endian-p = (logbitp 0 i)
133 | for name = (stream-float-ref-fun-name float-type readp big-endian-p)
134 | for n-bytes = (ecase float-type (double 8) (single 4))
135 | for single-fun = (if readp
136 | (float-ref-fun-name float-type big-endian-p)
137 | (float-set-fun-name float-type big-endian-p))
138 | for arglist = (if readp '(stream) '(float stream))
139 | for subfun = (if readp 'read-byte* 'write-byte*)
140 | for element-type = (ecase float-type (double 'double-float) (single 'single-float))
141 | collect `(defun ,name ,arglist
142 | (,subfun ,@arglist ,n-bytes #',single-fun)) into forms
143 | if readp
144 | collect `(defun ,(stream-float-seq-fun-name float-type t big-endian-p)
145 | (result-type stream count)
146 | ,(format-docstring "Return a sequence of type RESULT-TYPE, containing COUNT elements read from STREAM. Each element is a ~A read in ~:[little~;big~]-endian byte order. RESULT-TYPE must be either CL:VECTOR or CL:LIST. STREAM must have an element type of (UNSIGNED-BYTE 8)."
147 | element-type big-endian-p)
148 | (read-fresh-sequence result-type stream count
149 | ',element-type ,n-bytes #',single-fun)) into forms
150 | else
151 | collect `(defun ,(stream-float-seq-fun-name float-type nil big-endian-p)
152 | (seq stream &key (start 0) end)
153 | ,(format-docstring "Write elements from SEQ between START and END as ~As in ~:[little~;big~]-endian byte order to STREAM. SEQ may be either a vector or a list. STREAM must have an element type of (UNSIGNED-BYTE 8)."
154 | element-type big-endian-p)
155 | (write-sequence-with-writer seq stream start end #',name)) into forms
156 | if readp
157 | collect `(defun ,(stream-float-into-seq-fun-name float-type big-endian-p)
158 | (seq stream &key (start 0) end)
159 | ,(format-docstring "Destructively modify SEQ by replacing the elements of SEQ between START and END with elements read from STREAM. Each element is a ~A read in ~:[little~;big~]-endian byte order. SEQ may be either a vector or a list. STREAM must have na element type of (UNSIGNED-BYTE 8)."
160 | element-type big-endian-p)
161 | (read-into-sequence seq stream start end ,n-bytes #',single-fun)) into forms
162 | finally (return `(progn ,@forms)))
163 |
--------------------------------------------------------------------------------
/tests.lisp:
--------------------------------------------------------------------------------
1 | ;;;; tests.lisp -- tests for various bits of functionality
2 |
3 | (cl:defpackage :nibbles-tests
4 | (:use :cl))
5 |
6 | (cl:in-package :nibbles-tests)
7 |
8 | ;;; Basic tests for correctness.
9 |
10 | (defun make-byte-combiner (n-bytes big-endian-p)
11 | (let ((count 0)
12 | (buffer 0))
13 | #'(lambda (byte)
14 | (setf buffer
15 | (if big-endian-p
16 | (logior (ash buffer 8) byte)
17 | (let ((x (logior (ash byte (* 8 count)) buffer)))
18 | (if (= count n-bytes)
19 | (ash x -8)
20 | x))))
21 | (unless (= count n-bytes)
22 | (incf count))
23 | (cond
24 | ((= count n-bytes)
25 | (let ((val (ldb (byte (* 8 n-bytes) 0) buffer)))
26 | (multiple-value-prog1 (values val t)
27 | (setf buffer val))))
28 | (t (values 0 nil))))))
29 |
30 | (defun generate-random-octet-vector (n-octets)
31 | (loop with v = (nibbles:make-octet-vector n-octets)
32 | for i from 0 below n-octets
33 | do (setf (aref v i) (random 256))
34 | finally (return v)))
35 |
36 | (defun generate-reffed-values (byte-vector bitsize signedp big-endian-p
37 | &optional (rolling-p t))
38 | (do* ((byte-kind (if signedp 'signed-byte 'unsigned-byte))
39 | (bytesize (truncate bitsize 8))
40 | (n-bytes-to-read (if rolling-p
41 | (length byte-vector)
42 | (* (floor (length byte-vector) bytesize)
43 | bytesize)))
44 | (n-values (if rolling-p
45 | (- (length byte-vector) (1- bytesize))
46 | (truncate n-bytes-to-read bytesize)))
47 | (expected-values (make-array n-values :element-type `(,byte-kind ,bitsize)))
48 | (i 0 (1+ i))
49 | (j 0)
50 | (combiner (make-byte-combiner bytesize big-endian-p)))
51 | ((>= i n-bytes-to-read) expected-values)
52 | (multiple-value-bind (aggregate set-p) (funcall combiner (aref byte-vector i))
53 | (when set-p
54 | (setf (aref expected-values j)
55 | (if (and signedp (logbitp (1- bitsize) aggregate))
56 | (dpb aggregate (byte bitsize 0) -1)
57 | aggregate))
58 | (unless rolling-p
59 | (setf combiner (make-byte-combiner bytesize big-endian-p)))
60 | (incf j)))))
61 |
62 | (defvar *default-n-values* 4096)
63 |
64 | (defun generate-random-test (bitsize signedp big-endian-p
65 | &optional (n-values *default-n-values*))
66 | (let* ((n-bytes (truncate bitsize 8))
67 | (total-octets (+ n-values (1- n-bytes)))
68 | (random-octets (generate-random-octet-vector total-octets))
69 | (expected-vector
70 | (generate-reffed-values random-octets bitsize signedp big-endian-p)))
71 | (values random-octets expected-vector)))
72 |
73 | (defun compile-quietly (form)
74 | (handler-bind ((style-warning #'muffle-warning)
75 | #+sbcl (sb-ext:compiler-note #'muffle-warning))
76 | (compile nil form)))
77 |
78 | (defun ref-test (reffer bitsize signedp big-endian-p
79 | &optional (n-octets *default-n-values*))
80 | (multiple-value-bind (byte-vector expected-vector)
81 | (generate-random-test bitsize signedp big-endian-p n-octets)
82 | (flet ((run-test (reffer)
83 | (loop for i from 0 below n-octets
84 | for j from 0
85 | do (let ((reffed-val (funcall reffer byte-vector i))
86 | (expected-val (aref expected-vector j)))
87 | (unless (= reffed-val expected-val)
88 | (error "wanted ~D, got ~D from ~A"
89 | expected-val reffed-val
90 | (subseq byte-vector i
91 | (+ i (truncate bitsize 8))))))
92 | finally (return :ok))))
93 | (run-test reffer)
94 | (when (typep byte-vector '(simple-array (unsigned-byte 8) (*)))
95 | (let ((compiled (compile-quietly
96 | `(lambda (v i)
97 | (declare (type (simple-array (unsigned-byte 8) (*)) v))
98 | (declare (type (integer 0 #.(1- array-dimension-limit)) i))
99 | (declare (optimize speed (debug 0)))
100 | (,reffer v i)))))
101 | (run-test compiled))))))
102 |
103 | (defun set-test (reffer bitsize signedp big-endian-p
104 | &optional (n-octets *default-n-values*))
105 | ;; We use GET-SETF-EXPANSION to avoid reaching too deeply into
106 | ;; internals. This bit relies on knowing that the writer-form will be
107 | ;; a simple function call whose CAR is the internal setter, but I
108 | ;; think that's a bit better than :: references everywhere.
109 | (multiple-value-bind (vars vals store-vars writer-form reader-form)
110 | (get-setf-expansion `(,reffer x i))
111 | (declare (ignore vars vals store-vars reader-form))
112 | (let ((setter (car writer-form)))
113 | ;; Sanity check.
114 | (unless (eq (symbol-package setter) (find-package :nibbles))
115 | (error "need to update setter tests!"))
116 | (multiple-value-bind (byte-vector expected-vector)
117 | (generate-random-test bitsize signedp big-endian-p n-octets)
118 | (flet ((run-test (setter)
119 | (loop with fill-vec = (let ((v (copy-seq byte-vector)))
120 | (fill v 0)
121 | v)
122 | for i from 0 below n-octets
123 | for j from 0
124 | do (funcall setter fill-vec i (aref expected-vector j))
125 | finally (return
126 | (if (mismatch fill-vec byte-vector)
127 | (error "wanted ~A, got ~A" byte-vector fill-vec)
128 | :ok)))))
129 | (run-test setter)
130 | (when (typep byte-vector '(simple-array (unsigned-byte 8) (*)))
131 | (let ((compiled (compile-quietly
132 | `(lambda (v i new)
133 | (declare (type (simple-array (unsigned-byte 8) (*)) v))
134 | (declare (type (integer 0 #.(1- array-dimension-limit)) i))
135 | (declare (type (,(if signedp 'signed-byte 'unsigned-byte)
136 | ,bitsize) new))
137 | (declare (optimize speed (debug 0)))
138 | (,setter v i new)))))
139 | (run-test compiled))))))))
140 |
141 | ;;; Big-endian integer ref tests
142 |
143 | (rtest:deftest :ub16ref/be
144 | (ref-test 'nibbles:ub16ref/be 16 nil t)
145 | :ok)
146 |
147 | (rtest:deftest :sb16ref/be
148 | (ref-test 'nibbles:sb16ref/be 16 t t)
149 | :ok)
150 |
151 | (rtest:deftest :ub24ref/be
152 | (ref-test 'nibbles:ub24ref/be 24 nil t)
153 | :ok)
154 |
155 | (rtest:deftest :sb24ref/be
156 | (ref-test 'nibbles:sb24ref/be 24 t t)
157 | :ok)
158 |
159 | (rtest:deftest :ub32ref/be
160 | (ref-test 'nibbles:ub32ref/be 32 nil t)
161 | :ok)
162 |
163 | (rtest:deftest :sb32ref/be
164 | (ref-test 'nibbles:sb32ref/be 32 t t)
165 | :ok)
166 |
167 | (rtest:deftest :ub64ref/be
168 | (ref-test 'nibbles:ub64ref/be 64 nil t)
169 | :ok)
170 |
171 | (rtest:deftest :sb64ref/be
172 | (ref-test 'nibbles:sb64ref/be 64 t t)
173 | :ok)
174 |
175 | ;;; Big-endian integer set tests
176 |
177 | (rtest:deftest :ub16set/be
178 | (set-test 'nibbles:ub16ref/be 16 nil t)
179 | :ok)
180 |
181 | (rtest:deftest :sb16set/be
182 | (set-test 'nibbles:sb16ref/be 16 t t)
183 | :ok)
184 |
185 | (rtest:deftest :ub24set/be
186 | (set-test 'nibbles:ub24ref/be 24 nil t)
187 | :ok)
188 |
189 | (rtest:deftest :sb24set/be
190 | (set-test 'nibbles:sb24ref/be 24 t t)
191 | :ok)
192 |
193 | (rtest:deftest :ub32set/be
194 | (set-test 'nibbles:ub32ref/be 32 nil t)
195 | :ok)
196 |
197 | (rtest:deftest :sb32set/be
198 | (set-test 'nibbles:sb32ref/be 32 t t)
199 | :ok)
200 |
201 | (rtest:deftest :ub64set/be
202 | (set-test 'nibbles:ub64ref/be 64 nil t)
203 | :ok)
204 |
205 | (rtest:deftest :sb64set/be
206 | (set-test 'nibbles:sb64ref/be 64 t t)
207 | :ok)
208 |
209 | ;;; Little-endian integer ref tests
210 |
211 | (rtest:deftest :ub16ref/le
212 | (ref-test 'nibbles:ub16ref/le 16 nil nil)
213 | :ok)
214 |
215 | (rtest:deftest :sb16ref/le
216 | (ref-test 'nibbles:sb16ref/le 16 t nil)
217 | :ok)
218 |
219 | (rtest:deftest :ub24ref/le
220 | (ref-test 'nibbles:ub24ref/le 24 nil nil)
221 | :ok)
222 |
223 | (rtest:deftest :sb24ref/le
224 | (ref-test 'nibbles:sb24ref/le 24 t nil)
225 | :ok)
226 |
227 | (rtest:deftest :ub32ref/le
228 | (ref-test 'nibbles:ub32ref/le 32 nil nil)
229 | :ok)
230 |
231 | (rtest:deftest :sb32ref/le
232 | (ref-test 'nibbles:sb32ref/le 32 t nil)
233 | :ok)
234 |
235 | (rtest:deftest :ub64ref/le
236 | (ref-test 'nibbles:ub64ref/le 64 nil nil)
237 | :ok)
238 |
239 | (rtest:deftest :sb64ref/le
240 | (ref-test 'nibbles:sb64ref/le 64 t nil)
241 | :ok)
242 |
243 | ;;; Little-endian integer set tests
244 |
245 | (rtest:deftest :ub16set/le
246 | (set-test 'nibbles:ub16ref/le 16 nil nil)
247 | :ok)
248 |
249 | (rtest:deftest :sb16set/le
250 | (set-test 'nibbles:sb16ref/le 16 t nil)
251 | :ok)
252 |
253 | (rtest:deftest :ub24set/le
254 | (set-test 'nibbles:ub24ref/le 24 nil nil)
255 | :ok)
256 |
257 | (rtest:deftest :sb24set/le
258 | (set-test 'nibbles:sb24ref/le 24 t nil)
259 | :ok)
260 |
261 | (rtest:deftest :ub32set/le
262 | (set-test 'nibbles:ub32ref/le 32 nil nil)
263 | :ok)
264 |
265 | (rtest:deftest :sb32set/le
266 | (set-test 'nibbles:sb32ref/le 32 t nil)
267 | :ok)
268 |
269 | (rtest:deftest :ub64set/le
270 | (set-test 'nibbles:ub64ref/le 64 nil nil)
271 | :ok)
272 |
273 | (rtest:deftest :sb64set/le
274 | (set-test 'nibbles:sb64ref/le 64 t nil)
275 | :ok)
276 |
277 | ;;; Floating point.
278 |
279 | (defun normal-float-p (bits bitsize)
280 | "Return true when BITS represents a IEEE floating point number that is
281 | neither an infinity nor a NaN. Additionally, for CLISP, the number may not be
282 | denormalized."
283 | (ecase bitsize
284 | (32 (let ((exponent (ldb (byte 8 23) bits)))
285 | (and (/= exponent 255) #+clisp (/= exponent 0))))
286 | (64 (let ((exponent (ldb (byte 11 52) bits)))
287 | (and (/= exponent 2047) #+clisp (/= exponent 0))))))
288 |
289 | (defun random-float-bits (bitsize)
290 | (let ((bits (random (expt 2 bitsize))))
291 | (if (normal-float-p bits bitsize)
292 | bits
293 | (random-float-bits bitsize))))
294 |
295 | (defun generate-random-float-vector (n-floats bitsize big-endian-p)
296 | (let* ((bytesize (truncate bitsize 8))
297 | (octets (* n-floats bytesize))
298 | (v (nibbles:make-octet-vector octets)))
299 | (loop for i from 0 below octets by bytesize do
300 | (let ((bits (random-float-bits bitsize)))
301 | (ecase bitsize
302 | (32 (if big-endian-p
303 | (setf (nibbles:ub32ref/be v i) bits)
304 | (setf (nibbles:ub32ref/le v i) bits)))
305 | (64 (if big-endian-p
306 | (setf (nibbles:ub64ref/be v i) bits)
307 | (setf (nibbles:ub64ref/le v i) bits))))))
308 | v))
309 |
310 | (defun generate-reffed-floats (byte-vector n-floats bitsize big-endian-p)
311 | (let ((bytesize (truncate bitsize 8))
312 | (expected-values
313 | (make-array n-floats :element-type (if (= bitsize 32) 'single-float 'double-float))))
314 | (loop for i from 0 by bytesize
315 | for j from 0 below n-floats
316 | do (let ((bits
317 | (ecase bitsize
318 | (32 (if big-endian-p
319 | (nibbles:ub32ref/be byte-vector i)
320 | (nibbles:ub32ref/le byte-vector i)))
321 | (64 (if big-endian-p
322 | (nibbles:ub64ref/be byte-vector i)
323 | (nibbles:ub64ref/le byte-vector i))))))
324 | (setf (aref expected-values j)
325 | (ecase bitsize
326 | (32 (nibbles::make-single-float bits))
327 | (64 (nibbles::make-double-float (ldb (byte 32 32) bits)
328 | (ldb (byte 32 0) bits)))))))
329 | expected-values))
330 |
331 | (defvar *default-float-values* 4096)
332 |
333 | (defun generate-random-float-test (bitsize big-endian-p
334 | &optional (n-floats *default-float-values*))
335 | (let* ((random-octets (generate-random-float-vector n-floats bitsize big-endian-p))
336 | (expected-vector (generate-reffed-floats random-octets n-floats bitsize big-endian-p)))
337 | (values random-octets expected-vector)))
338 |
339 | (defun ref-float-test (reffer bitsize big-endian-p
340 | &optional (n-floats *default-float-values*))
341 | (multiple-value-bind (byte-vector expected-vector)
342 | (generate-random-float-test bitsize big-endian-p n-floats)
343 | (flet ((run-test (reffer)
344 | (let ((bytesize (truncate bitsize 8)))
345 | (loop for i from 0 by bytesize
346 | for j from 0 below n-floats
347 | do (let ((reffed-val (funcall reffer byte-vector i))
348 | (expected-val (aref expected-vector j)))
349 | (unless (= reffed-val expected-val)
350 | (error "wanted ~D, got ~D from ~A"
351 | expected-val reffed-val
352 | (subseq byte-vector i (+ i bytesize)))))
353 | finally (return :ok)))))
354 | (run-test reffer)
355 | (when (typep byte-vector '(simple-array (unsigned-byte 8) (*)))
356 | (let ((compiled (compile-quietly
357 | `(lambda (v i)
358 | (declare (type (simple-array (unsigned-byte 8) (*)) v))
359 | (declare (type (integer 0 #.(1- array-dimension-limit)) i))
360 | (declare (optimize speed (debug 0)))
361 | (,reffer v i)))))
362 | (run-test compiled))))))
363 |
364 | (defun set-float-test (reffer bitsize big-endian-p
365 | &optional (n-floats *default-float-values*))
366 | ;; We use GET-SETF-EXPANSION to avoid reaching too deeply into
367 | ;; internals. This bit relies on knowing that the writer-form will be
368 | ;; a simple function call whose CAR is the internal setter, but I
369 | ;; think that's a bit better than :: references everywhere.
370 | (multiple-value-bind (vars vals store-vars writer-form reader-form)
371 | (get-setf-expansion `(,reffer x i))
372 | (declare (ignore vars vals store-vars reader-form))
373 | (let ((setter (car writer-form)))
374 | ;; Sanity check.
375 | (unless (eq (symbol-package setter) (find-package :nibbles))
376 | (error "need to update setter tests!"))
377 | (multiple-value-bind (byte-vector expected-vector)
378 | (generate-random-float-test bitsize big-endian-p n-floats)
379 | (flet ((run-test (setter)
380 | (let ((bytesize (truncate bitsize 8)))
381 | (loop with fill-vec = (let ((v (copy-seq byte-vector)))
382 | (fill v 0)
383 | v)
384 | for i from 0 by bytesize
385 | for j from 0 below n-floats
386 | do (funcall setter fill-vec i (aref expected-vector j))
387 | finally (return
388 | (if (mismatch fill-vec byte-vector)
389 | (error "wanted ~A, got ~A" byte-vector fill-vec)
390 | :ok))))))
391 | (run-test setter)
392 | (when (typep byte-vector '(simple-array (unsigned-byte 8) (*)))
393 | (let ((compiled (compile-quietly
394 | `(lambda (v i new)
395 | (declare (type (simple-array (unsigned-byte 8) (*)) v))
396 | (declare (type (integer 0 #.(1- array-dimension-limit)) i))
397 | (declare (type ,(if (= bitsize 32) 'single-float 'double-float)
398 | new))
399 | (declare (optimize speed (debug 0)))
400 | (,setter v i new)))))
401 | (run-test compiled))))))))
402 |
403 | ;;; Big-endian float ref tests
404 |
405 | (rtest:deftest :ieee-single-ref/be
406 | (ref-float-test 'nibbles:ieee-single-ref/be 32 t)
407 | :ok)
408 |
409 | (rtest:deftest :ieee-double-ref/be
410 | (ref-float-test 'nibbles:ieee-double-ref/be 64 t)
411 | :ok)
412 |
413 | ;;; Big-endian float set tests
414 |
415 | (rtest:deftest :ieee-single-set/be
416 | (set-float-test 'nibbles:ieee-single-ref/be 32 t)
417 | :ok)
418 |
419 | (rtest:deftest :ieee-double-set/be
420 | (set-float-test 'nibbles:ieee-double-ref/be 64 t)
421 | :ok)
422 |
423 | ;;; Little-endian float ref tests
424 |
425 | (rtest:deftest :ieee-single-ref/le
426 | (ref-float-test 'nibbles:ieee-single-ref/le 32 nil)
427 | :ok)
428 |
429 | (rtest:deftest :ieee-double-ref/le
430 | (ref-float-test 'nibbles:ieee-double-ref/le 64 nil)
431 | :ok)
432 |
433 | ;;; Little-endian float set tests
434 |
435 | (rtest:deftest :ieee-single-set/le
436 | (set-float-test 'nibbles:ieee-single-ref/le 32 nil)
437 | :ok)
438 |
439 | (rtest:deftest :ieee-double-set/le
440 | (set-float-test 'nibbles:ieee-double-ref/le 64 nil)
441 | :ok)
442 |
443 |
444 | ;;; Stream reading tests
445 |
446 | (defvar *path* #.*compile-file-truename*)
447 |
448 | (defun read-file-as-octets (pathname)
449 | (with-open-file (stream pathname :direction :input
450 | :element-type '(unsigned-byte 8))
451 | (let ((v (nibbles:make-octet-vector (file-length stream))))
452 | (read-sequence v stream)
453 | v)))
454 |
455 | (defun read-test (reader bitsize signedp big-endian-p)
456 | (let* ((pathname *path*)
457 | (file-contents (read-file-as-octets pathname))
458 | (expected-values (generate-reffed-values file-contents bitsize
459 | signedp big-endian-p)))
460 | (with-open-file (stream pathname :direction :input
461 | :element-type '(unsigned-byte 8))
462 | (loop with n-values = (length expected-values)
463 | for i from 0 below n-values
464 | do (file-position stream i)
465 | (let ((read-value (funcall reader stream))
466 | (expected-value (aref expected-values i)))
467 | (unless (= read-value expected-value)
468 | (return :bad)))
469 | finally (return :ok)))))
470 |
471 | (defun read-sequence-test (result-type reader bitsize signedp big-endian-p)
472 | (let* ((pathname *path*)
473 | (file-contents (subseq (read-file-as-octets pathname) 0 8))
474 | (expected-values (generate-reffed-values file-contents bitsize
475 | signedp big-endian-p nil)))
476 | (with-open-file (stream pathname :direction :input
477 | :element-type '(unsigned-byte 8))
478 | (let* ((n-values (truncate (length file-contents)
479 | (truncate bitsize 8)))
480 | (read-values (funcall reader result-type stream n-values)))
481 | (if (or (not (typep read-values result-type))
482 | (mismatch read-values expected-values))
483 | :bad
484 | :ok)))))
485 |
486 | (rtest:deftest :read-ub16/be
487 | (read-test 'nibbles:read-ub16/be 16 nil t)
488 | :ok)
489 |
490 | (rtest:deftest :read-sb16/be
491 | (read-test 'nibbles:read-sb16/be 16 t t)
492 | :ok)
493 |
494 | (rtest:deftest :read-ub24/be
495 | (read-test 'nibbles:read-ub24/be 24 nil t)
496 | :ok)
497 |
498 | (rtest:deftest :read-sb24/be
499 | (read-test 'nibbles:read-sb24/be 24 t t)
500 | :ok)
501 |
502 | (rtest:deftest :read-ub32/be
503 | (read-test 'nibbles:read-ub32/be 32 nil t)
504 | :ok)
505 |
506 | (rtest:deftest :read-sb32/be
507 | (read-test 'nibbles:read-sb32/be 32 t t)
508 | :ok)
509 |
510 | (rtest:deftest :read-ub64/be
511 | (read-test 'nibbles:read-ub64/be 64 nil t)
512 | :ok)
513 |
514 | (rtest:deftest :read-sb64/be
515 | (read-test 'nibbles:read-sb64/be 64 t t)
516 | :ok)
517 |
518 | (rtest:deftest :read-ub16/le
519 | (read-test 'nibbles:read-ub16/le 16 nil nil)
520 | :ok)
521 |
522 | (rtest:deftest :read-sb16/le
523 | (read-test 'nibbles:read-sb16/le 16 t nil)
524 | :ok)
525 |
526 | (rtest:deftest :read-ub32/le
527 | (read-test 'nibbles:read-ub32/le 32 nil nil)
528 | :ok)
529 |
530 | (rtest:deftest :read-sb32/le
531 | (read-test 'nibbles:read-sb32/le 32 t nil)
532 | :ok)
533 |
534 | (rtest:deftest :read-ub64/le
535 | (read-test 'nibbles:read-ub64/le 64 nil nil)
536 | :ok)
537 |
538 | (rtest:deftest :read-sb64/le
539 | (read-test 'nibbles:read-sb64/le 64 t nil)
540 | :ok)
541 |
542 | (rtest:deftest :read-ub16/be-vector
543 | (read-sequence-test 'vector 'nibbles:read-ub16/be-sequence 16 nil t)
544 | :ok)
545 |
546 | (rtest:deftest :read-sb16/be-vector
547 | (read-sequence-test 'vector 'nibbles:read-sb16/be-sequence 16 t t)
548 | :ok)
549 |
550 | (rtest:deftest :read-ub24/be-vector
551 | (read-sequence-test 'vector 'nibbles:read-ub24/be-sequence 24 nil t)
552 | :ok)
553 |
554 | (rtest:deftest :read-sb24/be-vector
555 | (read-sequence-test 'vector 'nibbles:read-sb24/be-sequence 24 t t)
556 | :ok)
557 |
558 | (rtest:deftest :read-ub32/be-vector
559 | (read-sequence-test 'vector 'nibbles:read-ub32/be-sequence 32 nil t)
560 | :ok)
561 |
562 | (rtest:deftest :read-sb32/be-vector
563 | (read-sequence-test 'vector 'nibbles:read-sb32/be-sequence 32 t t)
564 | :ok)
565 |
566 | (rtest:deftest :read-ub64/be-vector
567 | (read-sequence-test 'vector 'nibbles:read-ub64/be-sequence 64 nil t)
568 | :ok)
569 |
570 | (rtest:deftest :read-sb64/be-vector
571 | (read-sequence-test 'vector 'nibbles:read-sb64/be-sequence 64 t t)
572 | :ok)
573 |
574 | (rtest:deftest :read-ub16/le-vector
575 | (read-sequence-test 'vector 'nibbles:read-ub16/le-sequence 16 nil nil)
576 | :ok)
577 |
578 | (rtest:deftest :read-sb16/le-vector
579 | (read-sequence-test 'vector 'nibbles:read-sb16/le-sequence 16 t nil)
580 | :ok)
581 |
582 | (rtest:deftest :read-ub24/le-vector
583 | (read-sequence-test 'vector 'nibbles:read-ub24/le-sequence 24 nil nil)
584 | :ok)
585 |
586 | (rtest:deftest :read-sb24/le-vector
587 | (read-sequence-test 'vector 'nibbles:read-sb24/le-sequence 24 t nil)
588 | :ok)
589 |
590 | (rtest:deftest :read-ub32/le-vector
591 | (read-sequence-test 'vector 'nibbles:read-ub32/le-sequence 32 nil nil)
592 | :ok)
593 |
594 | (rtest:deftest :read-sb32/le-vector
595 | (read-sequence-test 'vector 'nibbles:read-sb32/le-sequence 32 t nil)
596 | :ok)
597 |
598 | (rtest:deftest :read-ub64/le-vector
599 | (read-sequence-test 'vector 'nibbles:read-ub64/le-sequence 64 nil nil)
600 | :ok)
601 |
602 | (rtest:deftest :read-sb64/le-vector
603 | (read-sequence-test 'vector 'nibbles:read-sb64/le-sequence 64 t nil)
604 | :ok)
605 |
606 | (rtest:deftest :read-ub16/be-list
607 | (read-sequence-test 'list 'nibbles:read-ub16/be-sequence 16 nil t)
608 | :ok)
609 |
610 | (rtest:deftest :read-sb16/be-list
611 | (read-sequence-test 'list 'nibbles:read-sb16/be-sequence 16 t t)
612 | :ok)
613 |
614 | (rtest:deftest :read-ub24/be-list
615 | (read-sequence-test 'list 'nibbles:read-ub24/be-sequence 24 nil t)
616 | :ok)
617 |
618 | (rtest:deftest :read-sb24/be-list
619 | (read-sequence-test 'list 'nibbles:read-sb24/be-sequence 24 t t)
620 | :ok)
621 |
622 | (rtest:deftest :read-ub32/be-list
623 | (read-sequence-test 'list 'nibbles:read-ub32/be-sequence 32 nil t)
624 | :ok)
625 |
626 | (rtest:deftest :read-sb32/be-list
627 | (read-sequence-test 'list 'nibbles:read-sb32/be-sequence 32 t t)
628 | :ok)
629 |
630 | (rtest:deftest :read-ub64/be-list
631 | (read-sequence-test 'list 'nibbles:read-ub64/be-sequence 64 nil t)
632 | :ok)
633 |
634 | (rtest:deftest :read-sb64/be-list
635 | (read-sequence-test 'list 'nibbles:read-sb64/be-sequence 64 t t)
636 | :ok)
637 |
638 | (rtest:deftest :read-ub16/le-list
639 | (read-sequence-test 'list 'nibbles:read-ub16/le-sequence 16 nil nil)
640 | :ok)
641 |
642 | (rtest:deftest :read-sb16/le-list
643 | (read-sequence-test 'list 'nibbles:read-sb16/le-sequence 16 t nil)
644 | :ok)
645 |
646 | (rtest:deftest :read-ub24/le-list
647 | (read-sequence-test 'list 'nibbles:read-ub24/le-sequence 24 nil nil)
648 | :ok)
649 |
650 | (rtest:deftest :read-sb24/le-list
651 | (read-sequence-test 'list 'nibbles:read-sb24/le-sequence 24 t nil)
652 | :ok)
653 |
654 | (rtest:deftest :read-ub32/le-list
655 | (read-sequence-test 'list 'nibbles:read-ub32/le-sequence 32 nil nil)
656 | :ok)
657 |
658 | (rtest:deftest :read-sb32/le-list
659 | (read-sequence-test 'list 'nibbles:read-sb32/le-sequence 32 t nil)
660 | :ok)
661 |
662 | (rtest:deftest :read-ub64/le-list
663 | (read-sequence-test 'list 'nibbles:read-ub64/le-sequence 64 nil nil)
664 | :ok)
665 |
666 | (rtest:deftest :read-sb64/le-list
667 | (read-sequence-test 'list 'nibbles:read-sb64/le-sequence 64 t nil)
668 | :ok)
669 |
670 | ;;; Stream writing tests
671 |
672 | (defvar *output-directory*
673 | (merge-pathnames (make-pathname :name nil :type nil
674 | :directory '(:relative "test-output"))
675 | (make-pathname :directory (pathname-directory *path*))))
676 |
677 | (defun write-test (writer bitsize signedp big-endian-p)
678 | (multiple-value-bind (byte-vector expected-values)
679 | (generate-random-test bitsize signedp big-endian-p)
680 | (let ((tmpfile (make-pathname :name "tmp" :defaults *output-directory*)))
681 | (ensure-directories-exist tmpfile)
682 | (with-open-file (stream tmpfile :direction :output
683 | :element-type '(unsigned-byte 8)
684 | :if-does-not-exist :create
685 | :if-exists :supersede)
686 | (loop with n-values = (length expected-values)
687 | for i from 0 below n-values
688 | do (file-position stream i)
689 | (funcall writer (aref expected-values i) stream)))
690 | (let ((file-contents (read-file-as-octets tmpfile)))
691 | (delete-file tmpfile)
692 | (if (mismatch byte-vector file-contents)
693 | :bad
694 | :ok)))))
695 |
696 | (defun read-sequence-from-file (filename seq-type reader n-values)
697 | (with-open-file (stream filename :direction :input
698 | :element-type '(unsigned-byte 8)
699 | :if-does-not-exist :error)
700 | (funcall reader seq-type stream n-values)))
701 |
702 | (defun write-sequence-test (seq-type reader writer
703 | bitsize signedp big-endian-p)
704 | (multiple-value-bind (byte-vector expected-values)
705 | (generate-random-test bitsize signedp big-endian-p)
706 | (declare (ignore byte-vector))
707 | (let ((tmpfile (make-pathname :name "tmp" :defaults *output-directory*))
708 | (values-seq (coerce expected-values seq-type)))
709 | (ensure-directories-exist tmpfile)
710 | (flet ((run-random-test (values expected-start expected-end)
711 | (with-open-file (stream tmpfile :direction :output
712 | :element-type '(unsigned-byte 8)
713 | :if-does-not-exist :create
714 | :if-exists :supersede)
715 | (funcall writer values stream :start expected-start
716 | :end expected-end))
717 | (let ((file-contents (read-sequence-from-file tmpfile
718 | seq-type
719 | reader
720 | (- expected-end expected-start))))
721 | (mismatch values file-contents
722 | :start1 expected-start
723 | :end1 expected-end))))
724 | (let* ((block-size (truncate (length expected-values) 4))
725 | (upper-quartile (* block-size 3)))
726 | (unwind-protect
727 | (loop repeat 32
728 | when (run-random-test values-seq (random block-size)
729 | (+ upper-quartile
730 | (random block-size)))
731 | do (return :bad)
732 | finally (return :ok))
733 | (delete-file tmpfile)))))))
734 |
735 | (rtest:deftest :write-ub16/be
736 | (write-test 'nibbles:write-ub16/be 16 nil t)
737 | :ok)
738 |
739 | (rtest:deftest :write-sb16/be
740 | (write-test 'nibbles:write-sb16/be 16 t t)
741 | :ok)
742 |
743 | (rtest:deftest :write-ub24/be
744 | (write-test 'nibbles:write-ub24/be 24 nil t)
745 | :ok)
746 |
747 | (rtest:deftest :write-sb24/be
748 | (write-test 'nibbles:write-sb24/be 24 t t)
749 | :ok)
750 |
751 | (rtest:deftest :write-ub32/be
752 | (write-test 'nibbles:write-ub32/be 32 nil t)
753 | :ok)
754 |
755 | (rtest:deftest :write-sb32/be
756 | (write-test 'nibbles:write-sb32/be 32 t t)
757 | :ok)
758 |
759 | (rtest:deftest :write-ub64/be
760 | (write-test 'nibbles:write-ub64/be 64 nil t)
761 | :ok)
762 |
763 | (rtest:deftest :write-sb64/be
764 | (write-test 'nibbles:write-sb64/be 64 t t)
765 | :ok)
766 |
767 | (rtest:deftest :write-ub16/le
768 | (write-test 'nibbles:write-ub16/le 16 nil nil)
769 | :ok)
770 |
771 | (rtest:deftest :write-sb16/le
772 | (write-test 'nibbles:write-sb16/le 16 t nil)
773 | :ok)
774 |
775 | (rtest:deftest :write-ub24/le
776 | (write-test 'nibbles:write-ub24/le 24 nil nil)
777 | :ok)
778 |
779 | (rtest:deftest :write-sb24/le
780 | (write-test 'nibbles:write-sb24/le 24 t nil)
781 | :ok)
782 |
783 | (rtest:deftest :write-ub32/le
784 | (write-test 'nibbles:write-ub32/le 32 nil nil)
785 | :ok)
786 |
787 | (rtest:deftest :write-sb32/le
788 | (write-test 'nibbles:write-sb32/le 32 t nil)
789 | :ok)
790 |
791 | (rtest:deftest :write-ub64/le
792 | (write-test 'nibbles:write-ub64/le 64 nil nil)
793 | :ok)
794 |
795 | (rtest:deftest :write-sb64/le
796 | (write-test 'nibbles:write-sb64/le 64 t nil)
797 | :ok)
798 |
799 | (rtest:deftest :write-ub16/be-vector
800 | (write-sequence-test 'vector
801 | 'nibbles:read-ub16/be-sequence
802 | 'nibbles:write-ub16/be-sequence 16 nil t)
803 | :ok)
804 |
805 | (rtest:deftest :write-sb16/be-vector
806 | (write-sequence-test 'vector
807 | 'nibbles:read-sb16/be-sequence
808 | 'nibbles:write-sb16/be-sequence 16 t t)
809 | :ok)
810 |
811 | (rtest:deftest :write-ub24/be-vector
812 | (write-sequence-test 'vector
813 | 'nibbles:read-ub24/be-sequence
814 | 'nibbles:write-ub24/be-sequence 24 nil t)
815 | :ok)
816 |
817 | (rtest:deftest :write-sb24/be-vector
818 | (write-sequence-test 'vector
819 | 'nibbles:read-sb24/be-sequence
820 | 'nibbles:write-sb24/be-sequence 24 t t)
821 | :ok)
822 |
823 | (rtest:deftest :write-ub32/be-vector
824 | (write-sequence-test 'vector
825 | 'nibbles:read-ub32/be-sequence
826 | 'nibbles:write-ub32/be-sequence 32 nil t)
827 | :ok)
828 |
829 | (rtest:deftest :write-sb32/be-vector
830 | (write-sequence-test 'vector
831 | 'nibbles:read-sb32/be-sequence
832 | 'nibbles:write-sb32/be-sequence 32 t t)
833 | :ok)
834 |
835 | (rtest:deftest :write-ub64/be-vector
836 | (write-sequence-test 'vector
837 | 'nibbles:read-ub64/be-sequence
838 | 'nibbles:write-ub64/be-sequence 64 nil t)
839 | :ok)
840 |
841 | (rtest:deftest :write-sb64/be-vector
842 | (write-sequence-test 'vector
843 | 'nibbles:read-sb64/be-sequence
844 | 'nibbles:write-sb64/be-sequence 64 t t)
845 | :ok)
846 |
847 | (rtest:deftest :write-ub16/le-vector
848 | (write-sequence-test 'vector
849 | 'nibbles:read-ub16/le-sequence
850 | 'nibbles:write-ub16/le-sequence 16 nil nil)
851 | :ok)
852 |
853 | (rtest:deftest :write-sb16/le-vector
854 | (write-sequence-test 'vector
855 | 'nibbles:read-sb16/le-sequence
856 | 'nibbles:write-sb16/le-sequence 16 t nil)
857 | :ok)
858 |
859 | (rtest:deftest :write-ub24/le-vector
860 | (write-sequence-test 'vector
861 | 'nibbles:read-ub24/le-sequence
862 | 'nibbles:write-ub24/le-sequence 24 nil nil)
863 | :ok)
864 |
865 | (rtest:deftest :write-sb24/le-vector
866 | (write-sequence-test 'vector
867 | 'nibbles:read-sb24/le-sequence
868 | 'nibbles:write-sb24/le-sequence 24 t nil)
869 | :ok)
870 |
871 | (rtest:deftest :write-ub32/le-vector
872 | (write-sequence-test 'vector
873 | 'nibbles:read-ub32/le-sequence
874 | 'nibbles:write-ub32/le-sequence 32 nil nil)
875 | :ok)
876 |
877 | (rtest:deftest :write-sb32/le-vector
878 | (write-sequence-test 'vector
879 | 'nibbles:read-sb32/le-sequence
880 | 'nibbles:write-sb32/le-sequence 32 t nil)
881 | :ok)
882 |
883 | (rtest:deftest :write-ub64/le-vector
884 | (write-sequence-test 'vector
885 | 'nibbles:read-ub64/le-sequence
886 | 'nibbles:write-ub64/le-sequence 64 nil nil)
887 | :ok)
888 |
889 | (rtest:deftest :write-sb64/le-vector
890 | (write-sequence-test 'vector
891 | 'nibbles:read-sb64/le-sequence
892 | 'nibbles:write-sb64/le-sequence 64 t nil)
893 | :ok)
894 |
895 | (rtest:deftest :write-ub16/be-list
896 | (write-sequence-test 'list
897 | 'nibbles:read-ub16/be-sequence
898 | 'nibbles:write-ub16/be-sequence 16 nil t)
899 | :ok)
900 |
901 | (rtest:deftest :write-sb16/be-list
902 | (write-sequence-test 'list
903 | 'nibbles:read-sb16/be-sequence
904 | 'nibbles:write-sb16/be-sequence 16 t t)
905 | :ok)
906 |
907 | (rtest:deftest :write-ub24/be-list
908 | (write-sequence-test 'list
909 | 'nibbles:read-ub24/be-sequence
910 | 'nibbles:write-ub24/be-sequence 24 nil t)
911 | :ok)
912 |
913 | (rtest:deftest :write-sb24/be-list
914 | (write-sequence-test 'list
915 | 'nibbles:read-sb24/be-sequence
916 | 'nibbles:write-sb24/be-sequence 24 t t)
917 | :ok)
918 |
919 | (rtest:deftest :write-ub32/be-list
920 | (write-sequence-test 'list
921 | 'nibbles:read-ub32/be-sequence
922 | 'nibbles:write-ub32/be-sequence 32 nil t)
923 | :ok)
924 |
925 | (rtest:deftest :write-sb32/be-list
926 | (write-sequence-test 'list
927 | 'nibbles:read-sb32/be-sequence
928 | 'nibbles:write-sb32/be-sequence 32 t t)
929 | :ok)
930 |
931 | (rtest:deftest :write-ub64/be-list
932 | (write-sequence-test 'list
933 | 'nibbles:read-ub64/be-sequence
934 | 'nibbles:write-ub64/be-sequence 64 nil t)
935 | :ok)
936 |
937 | (rtest:deftest :write-sb64/be-list
938 | (write-sequence-test 'list
939 | 'nibbles:read-sb64/be-sequence
940 | 'nibbles:write-sb64/be-sequence 64 t t)
941 | :ok)
942 |
943 | (rtest:deftest :write-ub16/le-list
944 | (write-sequence-test 'list
945 | 'nibbles:read-ub16/le-sequence
946 | 'nibbles:write-ub16/le-sequence 16 nil nil)
947 | :ok)
948 |
949 | (rtest:deftest :write-sb16/le-list
950 | (write-sequence-test 'list
951 | 'nibbles:read-sb16/le-sequence
952 | 'nibbles:write-sb16/le-sequence 16 t nil)
953 | :ok)
954 |
955 | (rtest:deftest :write-ub24/le-list
956 | (write-sequence-test 'list
957 | 'nibbles:read-ub24/le-sequence
958 | 'nibbles:write-ub24/le-sequence 24 nil nil)
959 | :ok)
960 |
961 | (rtest:deftest :write-sb24/le-list
962 | (write-sequence-test 'list
963 | 'nibbles:read-sb24/le-sequence
964 | 'nibbles:write-sb24/le-sequence 24 t nil)
965 | :ok)
966 |
967 | (rtest:deftest :write-ub32/le-list
968 | (write-sequence-test 'list
969 | 'nibbles:read-ub32/le-sequence
970 | 'nibbles:write-ub32/le-sequence 32 nil nil)
971 | :ok)
972 |
973 | (rtest:deftest :write-sb32/le-list
974 | (write-sequence-test 'list
975 | 'nibbles:read-sb32/le-sequence
976 | 'nibbles:write-sb32/le-sequence 32 t nil)
977 | :ok)
978 |
979 | (rtest:deftest :write-ub64/le-list
980 | (write-sequence-test 'list
981 | 'nibbles:read-ub64/le-sequence
982 | 'nibbles:write-ub64/le-sequence 64 nil nil)
983 | :ok)
984 |
985 | (rtest:deftest :write-sb64/le-list
986 | (write-sequence-test 'list
987 | 'nibbles:read-sb64/le-sequence
988 | 'nibbles:write-sb64/le-sequence 64 t nil)
989 | :ok)
990 |
--------------------------------------------------------------------------------
/types.lisp:
--------------------------------------------------------------------------------
1 | ;;;; types.lisp -- various useful types
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (deftype octet ()
6 | '(unsigned-byte 8))
7 |
8 | (deftype index ()
9 | '(mod #.array-dimension-limit))
10 |
11 |
12 | ;;; Type `octet-vector' and constructors
13 | ;;
14 |
15 | (deftype octet-vector (&optional (length '*))
16 | `(array octet (,length)))
17 |
18 | (declaim (ftype (function (index &key (:initial-element octet)) (values octet-vector &optional))
19 | make-octet-vector)
20 | (inline make-octet-vector))
21 |
22 | (defun make-octet-vector (count
23 | &key
24 | (initial-element 0))
25 | "Make and return an `octet-vector' with COUNT elements.
26 |
27 | If supplied, INITIAL-ELEMENT is used to populate the vector. The value
28 | of INITIAL-ELEMENT has to of type `octet'. "
29 | (make-array count
30 | :element-type 'octet
31 | :initial-element initial-element))
32 |
33 | (declaim (ftype (function (&rest octet) (values octet-vector &optional))
34 | octet-vector)
35 | (inline octet-vector))
36 |
37 | (defun octet-vector (&rest args)
38 | "Make and return an `octet-vector' containing the elements ARGS.
39 | ARGS have to be of type `octet'."
40 | (make-array (length args)
41 | :element-type 'octet
42 | :initial-contents args
43 | :adjustable nil
44 | :fill-pointer nil))
45 |
46 |
47 | ;;; Type `simple-octet-vector'
48 | ;;
49 |
50 | (deftype simple-octet-vector (&optional (length '*))
51 | #+(or sbcl cmu) `(simple-array octet (,length))
52 | #-(or sbcl cmu) `(array octet (,length)))
53 |
--------------------------------------------------------------------------------
/vectors.lisp:
--------------------------------------------------------------------------------
1 | ;;;; vectors.lisp -- signed/unsigned byte accessors
2 |
3 | (cl:in-package :nibbles)
4 |
5 | (declaim (inline array-data-and-offsets))
6 | (defun array-data-and-offsets (v start end)
7 | "Like ARRAY-DISPLACEMENT, only more useful."
8 | #+cmu
9 | (lisp::with-array-data ((v v) (start start) (end end))
10 | (values v start end))
11 | #+sbcl
12 | (sb-kernel:with-array-data ((v v) (start start) (end end))
13 | (values v start end))
14 | #-(or cmu sbcl)
15 | (values v start (or end (length v))))
16 |
17 | (macrolet ((define-fetcher (bitsize signedp big-endian-p)
18 | (let ((ref-name (byte-ref-fun-name bitsize signedp big-endian-p))
19 | (bytes (truncate bitsize 8)))
20 | `(defun ,ref-name (vector index)
21 | (declare (type octet-vector vector))
22 | (declare (type (integer 0 ,(- array-dimension-limit bytes)) index))
23 | (multiple-value-bind (vector start end)
24 | (array-data-and-offsets vector index (+ index ,bytes))
25 | #+sbcl (declare (optimize (sb-c::insert-array-bounds-checks 0)))
26 | (declare (type (integer 0 ,(- array-dimension-limit bytes)) start))
27 | (declare (ignore end))
28 | ,(ref-form 'vector 'start bytes signedp big-endian-p)))))
29 | (define-storer (bitsize signedp big-endian-p)
30 | (let ((ref-name (byte-ref-fun-name bitsize signedp big-endian-p))
31 | (set-name (byte-set-fun-name bitsize signedp big-endian-p))
32 | (bytes (truncate bitsize 8)))
33 | `(progn
34 | (defun ,set-name (vector index value)
35 | (declare (type octet-vector vector))
36 | (declare (type (integer 0 ,(- array-dimension-limit bytes)) index))
37 | (declare (type (,(if signedp
38 | 'signed-byte
39 | 'unsigned-byte) ,bitsize) value))
40 | (multiple-value-bind (vector start end)
41 | (array-data-and-offsets vector index (+ index ,bytes))
42 | #+sbcl (declare (optimize (sb-c::insert-array-bounds-checks 0)))
43 | (declare (type (integer 0 ,(- array-dimension-limit bytes)) start))
44 | (declare (ignore end))
45 | ,(set-form 'vector 'start 'value bytes big-endian-p)))
46 | (defsetf ,ref-name ,set-name))))
47 | (define-fetchers-and-storers (bitsize)
48 | (loop for i from 0 below 4
49 | for signedp = (logbitp 1 i)
50 | for big-endian-p = (logbitp 0 i)
51 | collect `(define-fetcher ,bitsize ,signedp ,big-endian-p) into forms
52 | collect `(define-storer ,bitsize ,signedp ,big-endian-p) into forms
53 | finally (return `(progn ,@forms)))))
54 | (define-fetchers-and-storers 16)
55 | (define-fetchers-and-storers 24)
56 | (define-fetchers-and-storers 32)
57 | (define-fetchers-and-storers 64))
58 |
59 | #+sbcl (declaim (sb-ext:maybe-inline ieee-single-ref/be))
60 | (defun ieee-single-ref/be (vector index)
61 | #+abcl
62 | (system::make-single-float (sb32ref/be vector index))
63 | #+allegro
64 | (let ((high (ub16ref/be vector index))
65 | (low (ub16ref/be vector (+ index 2))))
66 | (excl:shorts-to-single-float high low))
67 | #+ccl
68 | (ccl::host-single-float-from-unsigned-byte-32 (ub32ref/be vector index))
69 | #+clasp
70 | (ext:bits-to-single-float (ub32ref/be vector index))
71 | #+clisp
72 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:single-float) (bits ffi:uint32)))
73 | (setf (ffi:slot (ffi:foreign-value u) 'bits) (ub32ref/be vector index))
74 | (ffi:slot (ffi:foreign-value u) 'value))
75 | #+cmu
76 | (kernel:make-single-float (sb32ref/be vector index))
77 | #+ecl
78 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
79 | (if (>= ext:+ecl-version-number+ 230909)
80 | (system::bits-single-float (ub32ref/be vector index))
81 | (make-single-float (ub32ref/be vector index)))
82 | #+lispworks
83 | (let* ((ub (ub32ref/be vector index))
84 | (v (sys:make-typed-aref-vector 4)))
85 | (declare (optimize (speed 3) (float 0) (safety 0)))
86 | (declare (dynamic-extent v))
87 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) ub)
88 | (sys:typed-aref 'single-float v 0))
89 | #+mezzano
90 | (mezzano.extensions:ieee-binary32-to-single-float (ub32ref/be vector index))
91 | #+sbcl
92 | (sb-kernel:make-single-float (sb32ref/be vector index))
93 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
94 | (make-single-float (ub32ref/be vector index)))
95 |
96 | #+sbcl (declaim (sb-ext:maybe-inline ieee-single-sef/be))
97 | (defun ieee-single-set/be (vector index value)
98 | #+abcl
99 | (progn
100 | (setf (sb32ref/be vector index) (system:single-float-bits value))
101 | value)
102 | #+allegro
103 | (multiple-value-bind (high low) (excl:single-float-to-shorts value)
104 | (setf (ub16ref/be vector index) high
105 | (ub16ref/be vector (+ index 2)) low)
106 | value)
107 | #+ccl
108 | (progn
109 | (setf (ub32ref/be vector index) (ccl::single-float-bits value))
110 | value)
111 | #+clasp
112 | (progn
113 | (setf (ub32ref/be vector index) (ext:single-float-to-bits value))
114 | value)
115 | #+clisp
116 | (let ((bits
117 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:single-float) (bits ffi:uint32)))
118 | (setf (ffi:slot (ffi:foreign-value u) 'value) value)
119 | (ffi:slot (ffi:foreign-value u) 'bits))))
120 | (setf (ub32ref/be vector index) bits)
121 | value)
122 | #+cmu
123 | (progn
124 | (setf (sb32ref/be vector index) (kernel:single-float-bits value))
125 | value)
126 | #+ecl
127 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
128 | (progn
129 | (setf (ub32ref/be vector index)
130 | (if (>= ext:+ecl-version-number+ 230909)
131 | (system::single-float-bits value)
132 | (single-float-bits value)))
133 | value)
134 | #+lispworks
135 | (let* ((v (sys:make-typed-aref-vector 4)))
136 | (declare (optimize (speed 3) (float 0) (safety 0)))
137 | (declare (dynamic-extent v))
138 | (setf (sys:typed-aref 'single-float v 0) value)
139 | (setf (ub32ref/be vector index) (sys:typed-aref '(unsigned-byte 32) v 0))
140 | value)
141 | #+mezzano
142 | (progn
143 | (setf (ub32ref/be vector index) (mezzano.extensions:single-float-to-ieee-binary32 value))
144 | value)
145 | #+sbcl
146 | (progn
147 | (setf (sb32ref/be vector index) (sb-kernel:single-float-bits value))
148 | value)
149 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
150 | (progn
151 | (setf (ub32ref/be vector index) (single-float-bits value))
152 | value))
153 | (defsetf ieee-single-ref/be ieee-single-set/be)
154 |
155 | #+sbcl (declaim (sb-ext:maybe-inline ieee-single-ref/le))
156 | (defun ieee-single-ref/le (vector index)
157 | #+abcl
158 | (system::make-single-float (sb32ref/le vector index))
159 | #+allegro
160 | (let ((low (ub16ref/le vector index))
161 | (high (ub16ref/le vector (+ index 2))))
162 | (excl:shorts-to-single-float high low))
163 | #+ccl
164 | (ccl::host-single-float-from-unsigned-byte-32 (ub32ref/le vector index))
165 | #+clasp
166 | (ext:bits-to-single-float (ub32ref/le vector index))
167 | #+clisp
168 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:single-float) (bits ffi:uint32)))
169 | (setf (ffi:slot (ffi:foreign-value u) 'bits) (ub32ref/le vector index))
170 | (ffi:slot (ffi:foreign-value u) 'value))
171 | #+cmu
172 | (kernel:make-single-float (sb32ref/le vector index))
173 | #+ecl
174 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
175 | (if (>= ext:+ecl-version-number+ 230909)
176 | (system::bits-single-float (ub32ref/le vector index))
177 | (make-single-float (ub32ref/le vector index)))
178 | #+lispworks
179 | (let* ((ub (ub32ref/le vector index))
180 | (v (sys:make-typed-aref-vector 4)))
181 | (declare (optimize (speed 3) (float 0) (safety 0)))
182 | (declare (dynamic-extent v))
183 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) ub)
184 | (sys:typed-aref 'single-float v 0))
185 | #+mezzano
186 | (mezzano.extensions:ieee-binary32-to-single-float (ub32ref/le vector index))
187 | #+sbcl
188 | (sb-kernel:make-single-float (sb32ref/le vector index))
189 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
190 | (make-single-float (ub32ref/le vector index))
191 | )
192 |
193 | #+sbcl (declaim (sb-ext:maybe-inline ieee-single-set/le))
194 | (defun ieee-single-set/le (vector index value)
195 | #+abcl
196 | (progn
197 | (setf (sb32ref/le vector index) (system:single-float-bits value))
198 | value)
199 | #+allegro
200 | (multiple-value-bind (high low) (excl:single-float-to-shorts value)
201 | (setf (ub16ref/le vector index) low
202 | (ub16ref/le vector (+ index 2)) high)
203 | value)
204 | #+ccl
205 | (progn
206 | (setf (ub32ref/le vector index) (ccl::single-float-bits value))
207 | value)
208 | #+clasp
209 | (progn
210 | (setf (ub32ref/le vector index) (ext:single-float-to-bits value))
211 | value)
212 | #+clisp
213 | (let ((bits
214 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:single-float) (bits ffi:uint32)))
215 | (setf (ffi:slot (ffi:foreign-value u) 'value) value)
216 | (ffi:slot (ffi:foreign-value u) 'bits))))
217 | (setf (ub32ref/le vector index) bits)
218 | value)
219 | #+cmu
220 | (progn
221 | (setf (sb32ref/le vector index) (kernel:single-float-bits value))
222 | value)
223 | #+ecl
224 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
225 | (progn
226 | (setf (ub32ref/le vector index)
227 | (if (>= ext:+ecl-version-number+ 230909)
228 | (system::single-float-bits value)
229 | (single-float-bits value)))
230 | value)
231 | #+lispworks
232 | (let* ((v (sys:make-typed-aref-vector 4)))
233 | (declare (optimize (speed 3) (float 0) (safety 0)))
234 | (declare (dynamic-extent v))
235 | (setf (sys:typed-aref 'single-float v 0) value)
236 | (setf (ub32ref/le vector index) (sys:typed-aref '(unsigned-byte 32) v 0))
237 | value)
238 | #+mezzano
239 | (progn
240 | (setf (ub32ref/le vector index) (mezzano.extensions:single-float-to-ieee-binary32 value))
241 | value)
242 | #+sbcl
243 | (progn
244 | (setf (sb32ref/le vector index) (sb-kernel:single-float-bits value))
245 | value)
246 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
247 | (progn
248 | (setf (ub32ref/le vector index) (single-float-bits value))
249 | value))
250 | (defsetf ieee-single-ref/le ieee-single-set/le)
251 |
252 | #+sbcl (declaim (sb-ext:maybe-inline ieee-double-ref/be))
253 | (defun ieee-double-ref/be (vector index)
254 | #+abcl
255 | (let ((upper (sb32ref/be vector index))
256 | (lower (ub32ref/be vector (+ index 4))))
257 | (system:make-double-float (logior (ash upper 32) lower)))
258 | #+allegro
259 | (let ((u3 (ub16ref/be vector index))
260 | (u2 (ub16ref/be vector (+ index 2)))
261 | (u1 (ub16ref/be vector (+ index 4)))
262 | (u0 (ub16ref/be vector (+ index 6))))
263 | (excl:shorts-to-double-float u3 u2 u1 u0))
264 | #+ccl
265 | (let ((upper (ub32ref/be vector index))
266 | (lower (ub32ref/be vector (+ index 4))))
267 | (ccl::double-float-from-bits upper lower))
268 | #+clasp
269 | (let ((upper (ub32ref/be vector index))
270 | (lower (ub32ref/be vector (+ index 4))))
271 | (ext:bits-to-double-float (logior (ash upper 32) lower)))
272 | #+clisp
273 | (let ((upper (ub32ref/be vector index))
274 | (lower (ub32ref/be vector (+ index 4))))
275 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:double-float) (bits ffi:uint64)))
276 | (setf (ffi:slot (ffi:foreign-value u) 'bits) (logior (ash upper 32) lower))
277 | (ffi:slot (ffi:foreign-value u) 'value)))
278 | #+cmu
279 | (let ((upper (sb32ref/be vector index))
280 | (lower (ub32ref/be vector (+ index 4))))
281 | (kernel:make-double-float upper lower))
282 | #+ecl
283 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
284 | (let ((upper (ub32ref/be vector index))
285 | (lower (ub32ref/be vector (+ index 4))))
286 | (if (>= ext:+ecl-version-number+ 230909)
287 | (system::bits-double-float (logior (ash upper 32) lower))
288 | (make-double-float upper lower)))
289 | #+lispworks
290 | (let* ((upper (ub32ref/be vector index))
291 | (lower (ub32ref/be vector (+ index 4)))
292 | (v (sys:make-typed-aref-vector 8)))
293 | (declare (optimize (speed 3) (float 0) (safety 0)))
294 | (declare (dynamic-extent v))
295 | #+little-endian
296 | (progn
297 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) lower)
298 | (setf (sys:typed-aref '(unsigned-byte 32) v 4) upper))
299 | #-little-endian
300 | (progn
301 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) upper)
302 | (setf (sys:typed-aref '(unsigned-byte 32) v 4) lower))
303 | (sys:typed-aref 'double-float v 0))
304 | #+mezzano
305 | (let ((upper (ub32ref/be vector index))
306 | (lower (ub32ref/be vector (+ index 4))))
307 | (mezzano.extensions:ieee-binary64-to-double-float (logior (ash upper 32) lower)))
308 | #+sbcl
309 | (let ((upper (sb32ref/be vector index))
310 | (lower (ub32ref/be vector (+ index 4))))
311 | (sb-kernel:make-double-float upper lower))
312 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
313 | (let ((upper (ub32ref/be vector index))
314 | (lower (ub32ref/be vector (+ index 4))))
315 | (make-double-float upper lower)))
316 |
317 | #+sbcl (declaim (sb-ext:maybe-inline ieee-double-set/be))
318 | (defun ieee-double-set/be (vector index value)
319 | #+abcl
320 | (progn
321 | (setf (ub32ref/be vector index) (system::double-float-high-bits value)
322 | (ub32ref/be vector (+ index 4)) (system::double-float-low-bits value))
323 | value)
324 | #+allegro
325 | (multiple-value-bind (us3 us2 us1 us0) (excl:double-float-to-shorts value)
326 | (setf (ub16ref/be vector index) us3
327 | (ub16ref/be vector (+ index 2)) us2
328 | (ub16ref/be vector (+ index 4)) us1
329 | (ub16ref/be vector (+ index 6)) us0)
330 | value)
331 | #+ccl
332 | (multiple-value-bind (upper lower) (ccl::double-float-bits value)
333 | (setf (ub32ref/be vector index) upper
334 | (ub32ref/be vector (+ index 4)) lower)
335 | value)
336 | #+clasp
337 | (let ((bits (ext:double-float-to-bits value)))
338 | (setf (ub32ref/be vector index) (ldb (byte 32 32) bits)
339 | (ub32ref/be vector (+ index 4)) (ldb (byte 32 0) bits))
340 | value)
341 | #+clisp
342 | (let ((bits
343 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:double-float) (bits ffi:uint64)))
344 | (setf (ffi:slot (ffi:foreign-value u) 'value) value)
345 | (ffi:slot (ffi:foreign-value u) 'bits))))
346 | (setf (ub32ref/be vector index) (ldb (byte 32 32) bits)
347 | (ub32ref/be vector (+ index 4)) (ldb (byte 32 0) bits))
348 | value)
349 | #+cmu
350 | (progn
351 | (setf (sb32ref/be vector index) (kernel:double-float-high-bits value)
352 | (ub32ref/be vector (+ index 4)) (kernel:double-float-low-bits value))
353 | value)
354 | #+ecl
355 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
356 | (if (>= ext:+ecl-version-number+ 230909)
357 | (let ((bits (system::double-float-bits value)))
358 | (setf (ub32ref/be vector index) (ldb (byte 32 32) bits)
359 | (ub32ref/be vector (+ index 4)) (ldb (byte 32 0) bits))
360 | value)
361 | (multiple-value-bind (upper lower) (double-float-bits value)
362 | (setf (ub32ref/be vector index) upper
363 | (ub32ref/be vector (+ index 4)) lower)
364 | value))
365 | #+lispworks
366 | (let* ((v (sys:make-typed-aref-vector 8)))
367 | (declare (optimize (speed 3) (float 0) (safety 0)))
368 | (declare (dynamic-extent v))
369 | (setf (sys:typed-aref 'double-float v 0) value)
370 | #+little-endian
371 | (progn
372 | (setf (ub32ref/be vector index) (sys:typed-aref '(unsigned-byte 32) v 4)
373 | (ub32ref/be vector (+ index 4)) (sys:typed-aref '(unsigned-byte 32) v 0)))
374 | #-little-endian
375 | (progn
376 | (setf (ub32ref/be vector index) (sys:typed-aref '(unsigned-byte 32) v 0)
377 | (ub32ref/be vector (+ index 4)) (sys:typed-aref '(unsigned-byte 32) v 4)))
378 | value)
379 | #+mezzano
380 | (let ((bits (mezzano.extensions:double-float-to-ieee-binary64 value)))
381 | (setf (ub32ref/be vector index) (ldb (byte 32 32) bits)
382 | (ub32ref/be vector (+ index 4)) (ldb (byte 32 0) bits))
383 | value)
384 | #+sbcl
385 | (progn
386 | (setf (sb32ref/be vector index) (sb-kernel:double-float-high-bits value)
387 | (ub32ref/be vector (+ index 4)) (sb-kernel:double-float-low-bits value))
388 | value)
389 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
390 | (multiple-value-bind (upper lower) (double-float-bits value)
391 | (setf (ub32ref/be vector index) upper
392 | (ub32ref/be vector (+ index 4)) lower)
393 | value))
394 | (defsetf ieee-double-ref/be ieee-double-set/be)
395 |
396 | #+sbcl (declaim (sb-ext:maybe-inline ieee-double-ref/le))
397 | (defun ieee-double-ref/le (vector index)
398 | #+abcl
399 | (let ((lower (ub32ref/le vector index))
400 | (upper (sb32ref/le vector (+ index 4))))
401 | (system:make-double-float (logior (ash upper 32) lower)))
402 | #+allegro
403 | (let ((u0 (ub16ref/le vector index))
404 | (u1 (ub16ref/le vector (+ index 2)))
405 | (u2 (ub16ref/le vector (+ index 4)))
406 | (u3 (ub16ref/le vector (+ index 6))))
407 | (excl:shorts-to-double-float u3 u2 u1 u0))
408 | #+ccl
409 | (let ((lower (ub32ref/le vector index))
410 | (upper (ub32ref/le vector (+ index 4))))
411 | (ccl::double-float-from-bits upper lower))
412 | #+clasp
413 | (let ((lower (ub32ref/le vector index))
414 | (upper (ub32ref/le vector (+ index 4))))
415 | (ext:bits-to-double-float (logior (ash upper 32) lower)))
416 | #+clisp
417 | (let ((lower (ub32ref/le vector index))
418 | (upper (ub32ref/le vector (+ index 4))))
419 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:double-float) (bits ffi:uint64)))
420 | (setf (ffi:slot (ffi:foreign-value u) 'bits) (logior (ash upper 32) lower))
421 | (ffi:slot (ffi:foreign-value u) 'value)))
422 | #+cmu
423 | (let ((lower (ub32ref/le vector index))
424 | (upper (sb32ref/le vector (+ index 4))))
425 | (kernel:make-double-float upper lower))
426 | #+ecl
427 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
428 | (let ((lower (ub32ref/le vector index))
429 | (upper (ub32ref/le vector (+ index 4))))
430 | (if (>= ext:+ecl-version-number+ 230909)
431 | (system::bits-double-float (logior (ash upper 32) lower))
432 | (make-double-float upper lower)))
433 | #+lispworks
434 | (let* ((lower (ub32ref/le vector index))
435 | (upper (ub32ref/le vector (+ index 4)))
436 | (v (sys:make-typed-aref-vector 8)))
437 | (declare (optimize (speed 3) (float 0) (safety 0)))
438 | (declare (dynamic-extent v))
439 | #+little-endian
440 | (progn
441 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) lower)
442 | (setf (sys:typed-aref '(unsigned-byte 32) v 4) upper))
443 | #-little-endian
444 | (progn
445 | (setf (sys:typed-aref '(unsigned-byte 32) v 0) upper)
446 | (setf (sys:typed-aref '(unsigned-byte 32) v 4) lower))
447 | (sys:typed-aref 'double-float v 0))
448 | #+mezzano
449 | (let ((lower (ub32ref/le vector index))
450 | (upper (ub32ref/le vector (+ index 4))))
451 | (mezzano.extensions:ieee-binary64-to-double-float (logior (ash upper 32) lower)))
452 | #+sbcl
453 | (let ((lower (ub32ref/le vector index))
454 | (upper (sb32ref/le vector (+ index 4))))
455 | (sb-kernel:make-double-float upper lower))
456 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
457 | (let ((lower (ub32ref/le vector index))
458 | (upper (ub32ref/le vector (+ index 4))))
459 | (make-double-float upper lower)))
460 |
461 | #+sbcl (declaim (sb-ext:maybe-inline ieee-double-set/le))
462 | (defun ieee-double-set/le (vector index value)
463 | #+abcl
464 | (progn
465 | (setf (ub32ref/le vector index) (system::double-float-low-bits value)
466 | (ub32ref/le vector (+ index 4)) (system::double-float-high-bits value))
467 | value)
468 | #+allegro
469 | (multiple-value-bind (us3 us2 us1 us0) (excl:double-float-to-shorts value)
470 | (setf (ub16ref/le vector index) us0
471 | (ub16ref/le vector (+ index 2)) us1
472 | (ub16ref/le vector (+ index 4)) us2
473 | (ub16ref/le vector (+ index 6)) us3)
474 | value)
475 | #+ccl
476 | (multiple-value-bind (upper lower) (ccl::double-float-bits value)
477 | (setf (ub32ref/le vector index) lower
478 | (ub32ref/le vector (+ index 4)) upper)
479 | value)
480 | #+clasp
481 | (let ((bits (ext:double-float-to-bits value)))
482 | (setf (ub32ref/le vector index) (ldb (byte 32 0) bits)
483 | (ub32ref/le vector (+ index 4)) (ldb (byte 32 32) bits))
484 | value)
485 | #+clisp
486 | (let ((bits
487 | (ffi:with-foreign-object (u '(ffi:c-union (value ffi:double-float) (bits ffi:uint64)))
488 | (setf (ffi:slot (ffi:foreign-value u) 'value) value)
489 | (ffi:slot (ffi:foreign-value u) 'bits))))
490 | (setf (ub32ref/le vector index) (ldb (byte 32 0) bits)
491 | (ub32ref/le vector (+ index 4)) (ldb (byte 32 32) bits))
492 | value)
493 | #+cmu
494 | (progn
495 | (setf (ub32ref/le vector index) (kernel:double-float-low-bits value)
496 | (sb32ref/le vector (+ index 4)) (kernel:double-float-high-bits value))
497 | value)
498 | #+ecl
499 | ;; TODO: Remove version check when ECL version 23.9.9 or later is generally available.
500 | (if (>= ext:+ecl-version-number+ 230909)
501 | (let ((bits (system::double-float-bits value)))
502 | (setf (ub32ref/le vector index) (ldb (byte 32 0) bits)
503 | (ub32ref/le vector (+ index 4)) (ldb (byte 32 32) bits))
504 | value)
505 | (multiple-value-bind (upper lower) (double-float-bits value)
506 | (setf (ub32ref/le vector index) lower
507 | (ub32ref/le vector (+ index 4)) upper)
508 | value))
509 | #+lispworks
510 | (let* ((v (sys:make-typed-aref-vector 8)))
511 | (declare (optimize (speed 3) (float 0) (safety 0)))
512 | (declare (dynamic-extent v))
513 | (setf (sys:typed-aref 'double-float v 0) value)
514 | #+little-endian
515 | (progn
516 | (setf (ub32ref/le vector index) (sys:typed-aref '(unsigned-byte 32) v 0)
517 | (ub32ref/le vector (+ index 4)) (sys:typed-aref '(unsigned-byte 32) v 4)))
518 | #-little-endian
519 | (progn
520 | (setf (ub32ref/le vector index) (sys:typed-aref '(unsigned-byte 32) v 4)
521 | (ub32ref/le vector (+ index 4)) (sys:typed-aref '(unsigned-byte 32) v 0)))
522 | value)
523 | #+mezzano
524 | (let ((bits (mezzano.extensions:double-float-to-ieee-binary64 value)))
525 | (setf (ub32ref/le vector index) (ldb (byte 32 0) bits)
526 | (ub32ref/le vector (+ index 4)) (ldb (byte 32 32) bits))
527 | value)
528 | #+sbcl
529 | (progn
530 | (setf (ub32ref/le vector index) (sb-kernel:double-float-low-bits value)
531 | (sb32ref/le vector (+ index 4)) (sb-kernel:double-float-high-bits value))
532 | value)
533 | #-(or abcl allegro ccl clasp clisp cmu ecl lispworks mezzano sbcl)
534 | (multiple-value-bind (upper lower) (double-float-bits value)
535 | (setf (ub32ref/le vector index) lower
536 | (ub32ref/le vector (+ index 4)) upper)
537 | value))
538 | (defsetf ieee-double-ref/le ieee-double-set/le)
539 |
--------------------------------------------------------------------------------