Update AllegroCL for :long-long on 64-bit platforms
[uffi.git] / src / strings.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          strings.lisp
6 ;;;; Purpose:       UFFI source to handle strings, cstrings, and foreigns
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Feb 2002
9 ;;;;
10 ;;;; This file, part of UFFI, is Copyright (c) 2002-2010 by Kevin M. Rosenberg
11 ;;;; *************************************************************************
12
13 (in-package #:uffi)
14
15
16 (def-pointer-var +null-cstring-pointer+
17     #+(or cmu sbcl scl) nil
18     #+allegro 0
19     #+lispworks (fli:make-pointer :address 0 :type '(:unsigned :char))
20     #+(or openmcl digitool) (ccl:%null-ptr)
21 )
22
23 (defmacro convert-from-cstring (obj)
24   "Converts a string from a c-call. Same as convert-from-foreign-string, except
25 that LW/CMU automatically converts strings from c-calls."
26   #+(or cmu sbcl lispworks scl) obj
27   #+allegro
28   (let ((stored (gensym)))
29     `(let ((,stored ,obj))
30        (if (zerop ,stored)
31            nil
32            (values (excl:native-to-string ,stored)))))
33   #+(or openmcl digitool)
34   (let ((stored (gensym)))
35     `(let ((,stored ,obj))
36        (if (ccl:%null-ptr-p ,stored)
37            nil
38          (values (ccl:%get-cstring ,stored)))))
39   )
40
41 (defmacro convert-to-cstring (obj)
42   #+(or cmu sbcl scl lispworks) obj
43   #+allegro
44   (let ((stored (gensym)))
45     `(let ((,stored ,obj))
46        (if (null ,stored)
47            0
48            (values (excl:string-to-native ,stored)))))
49   #+(or openmcl digitool)
50   (let ((stored (gensym)))
51     `(let ((,stored ,obj))
52        (if (null ,stored)
53            +null-cstring-pointer+
54            (let ((ptr (new-ptr (1+ (length ,stored)))))
55              (ccl::%put-cstring ptr ,stored)
56              ptr))))
57   )
58
59 (defmacro free-cstring (obj)
60   (declare (ignorable obj))
61   #+allegro
62   (let ((stored (gensym)))
63     `(let ((,stored ,obj))
64        (unless (zerop ,stored)
65          (ff:free-fobject ,stored))))
66   #+(or openmcl digitool)
67   (let ((stored (gensym)))
68     `(let ((,stored ,obj))
69        (unless (ccl:%null-ptr-p ,stored)
70          (dispose-ptr ,stored))))
71   )
72
73 (defmacro with-cstring ((cstring lisp-string) &body body)
74   #+(or cmu sbcl scl lispworks)
75   `(let ((,cstring ,lisp-string)) ,@body)
76   #+allegro
77   (let ((acl-native (gensym))
78         (stored-lisp-string (gensym)))
79     `(let ((,stored-lisp-string ,lisp-string))
80        (excl:with-native-string (,acl-native ,stored-lisp-string)
81          (let ((,cstring (if ,stored-lisp-string ,acl-native 0)))
82            ,@body))))
83   #+(or openmcl digitool)
84   (let ((stored-lisp-string (gensym)))
85     `(let ((,stored-lisp-string ,lisp-string))
86        (if (stringp ,stored-lisp-string)
87            (ccl:with-cstrs ((,cstring ,stored-lisp-string))
88              ,@body)
89            (let ((,cstring +null-cstring-pointer+))
90              ,@body))))
91   )
92
93 (defmacro with-cstrings (bindings &rest body)
94   (if bindings
95       `(with-cstring ,(car bindings)
96         (with-cstrings ,(cdr bindings)
97           ,@body))
98       `(progn ,@body)))
99
100 ;;; Foreign string functions
101
102 (defun %convert-to-foreign-string (str encoding)
103   (declare (ignorable str encoding))
104   #+(or cmu scl)
105   (etypecase str
106     (null
107      (alien:sap-alien (system:int-sap 0) (* (alien:unsigned 8))))
108     (string
109      (locally
110          (declare (optimize (speed 3) (safety 0)))
111        (let* ((size (length str))
112               (storage (alien:make-alien (alien:unsigned 8) (1+ size))))
113          (declare (fixnum size))
114          (setq storage (alien:cast storage (* (alien:unsigned 8))))
115          (dotimes (i size)
116            (declare (fixnum i))
117            (setf (alien:deref storage i)
118                  (char-code (char str i))))
119          (setf (alien:deref storage size) 0)
120          storage))))
121
122   #+(and sbcl (not sb-unicode))
123   (etypecase str
124     (null
125      (sb-alien:sap-alien (sb-sys:int-sap 0) (* (sb-alien:unsigned 8))))
126     (string
127      (locally
128          (declare (optimize (speed 3) (safety 0)))
129        (let* ((size (length str))
130               (storage (sb-alien:make-alien (sb-alien:unsigned 8) (1+ size))))
131          (declare (fixnum i))
132          (setq storage (sb-alien:cast storage (* (sb-alien:unsigned 8))))
133          (dotimes (i size)
134            (declare (fixnum i))
135            (setf (sb-alien:deref storage i)
136                  (char-code (char str i))))
137          (setf (sb-alien:deref storage size) 0))
138        storage)))
139
140   #+(and sbcl sb-unicode)
141   (etypecase str
142     (null
143      (sb-alien:sap-alien (sb-sys:int-sap 0) (* (sb-alien:unsigned 8))))
144     (string
145      (locally
146          (declare (optimize (speed 3) (safety 0)))
147        (let* ((fe (or encoding *default-foreign-encoding*
148                       sb-impl::*default-external-format*))
149               (ife (when fe (lookup-foreign-encoding fe))))
150          (if ife
151              (let* ((octets (sb-ext:string-to-octets str :external-format ife))
152                     (size (length octets))
153                     (storage (sb-alien:make-alien (sb-alien:unsigned 8) (+ size 2))))
154                (declare (fixnum size))
155                (setq storage (sb-alien:cast storage (* (sb-alien:unsigned 8))))
156                (dotimes (i size)
157                  (declare (fixnum i))
158                  (setf (sb-alien:deref storage i) (aref octets i)))
159                ;; terminate with 2 nulls, maybe needed for some encodings
160                (setf (sb-alien:deref storage size) 0)
161                (setf (sb-alien:deref storage (1+ size)) 0)
162                storage)
163
164              (let* ((size (length str))
165                     (storage (sb-alien:make-alien (sb-alien:unsigned 8) (1+ size))))
166                (declare (fixnum size))
167                (setq storage (sb-alien:cast storage (* (sb-alien:unsigned 8))))
168                (dotimes (i size)
169                  (declare (fixnum i))
170                  (setf (sb-alien:deref storage i)
171                        (char-code (char str i))))
172                (setf (sb-alien:deref storage size) 0)
173                storage))))))
174
175   #+(and openmcl openmcl-unicode-strings)
176   (if (null str)
177       +null-cstring-pointer+
178       (locally
179           (declare (optimize (speed 3) (safety 0)))
180         (let* ((fe (or encoding *default-foreign-encoding*))
181                (ife (when fe (lookup-foreign-encoding fe))))
182           (if ife
183               (let* ((octets (ccl:encode-string-to-octets str :external-format ife))
184                      (size (length octets))
185                      (ptr (new-ptr (+ size 2))))
186                 (declare (fixnum size))
187                 (dotimes (i size)
188                   (declare (fixnum i))
189                   (setf (ccl:%get-unsigned-byte ptr i) (svref octets i)))
190                 (setf (ccl:%get-unsigned-byte ptr size) 0)
191                 (setf (ccl:%get-unsigned-byte ptr (1+ size)) 0)
192                 ptr)
193
194               (let ((ptr (new-ptr (1+ (length str)))))
195                 (ccl::%put-cstring ptr str)
196                 ptr)))))
197
198   #+(or digitool (and openmcl (not openmcl-unicode-strings)))
199   (if (null str)
200       +null-cstring-pointer+
201       (let ((ptr (new-ptr (1+ (length str)))))
202         (ccl::%put-cstring ptr str)
203         ptr))
204
205   #+(or allegro lispworks)
206   nil
207   )
208
209 (defmacro convert-to-foreign-string (obj &optional encoding)
210   (declare (ignorable encoding))
211   #+allegro
212   (let ((stored (gensym "STR-"))
213         (fe (gensym "FE-"))
214         (ife (gensym "IFE-")))
215     `(let* ((,stored ,obj)
216             (,fe (or encoding *default-foreign-encoding*))
217             (,ife (when ,fe
218                     (lookup-foreign-encoding ,fe))))
219        (cond
220          ((null ,stored)
221           0)
222          ((null ,ife)
223           (values (excl:string-to-native ,stored)))
224          (t
225            (values (excl:string-to-native ,stored :external-format ,ife))))))
226
227   #+lispworks
228   (let ((stored (gensym "STR-"))
229         (fe (gensym "EF-"))
230         (ife (gensym "NEF-")))
231     `(let* ((,stored ,obj)
232             (,fe (or ,encoding *default-foreign-encoding*))
233             (,ife (when ,fe
234                     (lookup-foreign-encoding ,fe))))
235        (cond
236          ((null ,stored)
237           +null-cstring-pointer+)
238          ((null ,ife)
239           (fli:convert-to-foreign-string ,stored))
240          (t
241           (fli:convert-to-foreign-string ,stored :external-format ,ife)))))
242
243   #+(or cmu scl sbcl digitool openmcl)
244   `(%convert-to-foreign-string ,obj (lookup-foreign-encoding
245                                      (or ,encoding *default-foreign-encoding*)))
246 )
247
248
249 ;; Either length or null-terminated-p must be non-nil
250 (defmacro convert-from-foreign-string (obj &key
251                                        length
252                                        encoding
253                                        (null-terminated-p t))
254   (declare (ignorable length encoding null-terminated-p))
255   #+allegro
256   (let ((stored-obj (gensym "STR-"))
257         (fe (gensym "FE-"))
258         (ife (gensym "IFE-")))
259     `(let ((,stored-obj ,obj))
260        (if (zerop ,stored-obj)
261            nil
262            (let* ((,fe (or ,encoding *default-foreign-encoding*))
263                   (,ife (when ,fe (lookup-foreign-encoding ,fe))))
264              (if ,ife
265                  (values
266                   (excl:native-to-string
267                    ,stored-obj
268                    ,@(when length (list :length length))
269                    :truncate (not ,null-terminated-p)
270                    :external-format ,ife))
271                  (fast-native-to-string ,stored-obj ,length))))))
272
273   #+lispworks
274   #|
275   ;; FAST-NATIVE-TO-STRING (suprisingly) works just fine to make strings
276   ;; for formatted printing with Lispworks and UTF-8 multibyte character strings.
277   ;; However, without knowledge of specific-encoding, the LENGTH call in FAST-NATIVE-TO-STRING
278   ;; will be be incorrect for some encodings/strings and strings consist of octets rather
279   ;; than wide characters
280   ;; This is a stop-gap until get tech support on why the below fails.
281   (let ((stored-obj (gensym "STR-")))
282     `(let ((,stored-obj ,obj))
283        (if (fli:null-pointer-p ,stored-obj)
284            nil
285            (fast-native-to-string ,stored-obj ,length))))
286   |#
287   #|
288   ;; Below code doesn't work on tesing with LW 6.0 testing with a UTF-8 string.
289   ;; fli:convert-from-foreign-string with :external-format of :UTF-8 doesn't
290   ;; properly code multibyte characters.
291   |#
292   (let ((stored-obj (gensym "STR-"))
293         (fe (gensym "FE-"))
294         (ife (gensym "IFE-")))
295     `(let ((,stored-obj ,obj))
296        (if (fli:null-pointer-p ,stored-obj)
297            nil
298            (let* ((,fe (or ,encoding *default-foreign-encoding*))
299                   (,ife (when ,fe (lookup-foreign-encoding ,fe))))
300              (if ,ife
301                  (fli:convert-from-foreign-string
302                   ,stored-obj
303                   ,@(when length (list :length length))
304                   :null-terminated-p ,null-terminated-p
305                   :external-format (list ,ife :eol-style :lf))
306                  (fast-native-to-string ,stored-obj ,length))))))
307
308   #+(or cmu scl)
309   (let ((stored-obj (gensym)))
310     `(let ((,stored-obj ,obj))
311        (if (null-pointer-p ,stored-obj)
312            nil
313            (cmucl-naturalize-cstring (alien:alien-sap ,stored-obj)
314                                      :length ,length
315                                      :null-terminated-p ,null-terminated-p))))
316   #+(and sbcl (not sb-unicode))
317   (let ((stored-obj (gensym)))
318     `(let ((,stored-obj ,obj))
319        (if (null-pointer-p ,stored-obj)
320            nil
321            (sbcl-naturalize-cstring (sb-alien:alien-sap ,stored-obj)
322                                     :length ,length
323                                     :null-terminated-p ,null-terminated-p))))
324
325   #+(and sbcl sb-unicode)
326   (let ((stored-obj (gensym "STR-"))
327         (fe (gensym "FE-"))
328         (ife (gensym "IFE-")))
329     `(let ((,stored-obj ,obj))
330        (if (null-pointer-p ,stored-obj)
331            nil
332            (let* ((,fe (or ,encoding *default-foreign-encoding*))
333                   (,ife (when ,fe (lookup-foreign-encoding ,fe))))
334              (sb-alien::c-string-to-string (sb-alien:alien-sap ,stored-obj)
335                                            (or ,ife sb-impl::*default-external-format* :latin-1)
336                                            'character)))))
337
338   #+(or openmcl digitool)
339   (let ((stored-obj (gensym "STR-"))
340         (fe (gensym "FE-")))
341     `(let ((,stored-obj ,obj))
342        (if (ccl:%null-ptr-p ,stored-obj)
343            nil
344            #+digitool
345            (ccl:%get-cstring
346             ,stored-obj 0
347             ,@(if length (list length) nil))
348            #+openmcl
349            (let ((,fe (or ,encoding *default-foreign-encoding*)))
350              (case ,fe
351                (:utf-8
352                 (ccl::%get-utf-8-cstring ,stored-obj))
353                (:ucs-2
354                 (ccl::%get-native-utf-16-cstring ,stored-obj))
355                (t
356                  ,@(if length
357                        `((ccl:%str-from-ptr ,stored-obj ,length))
358                        `((ccl:%get-cstring ,stored-obj)))))))))
359   )
360
361
362 (defmacro allocate-foreign-string (size &key (unsigned t))
363   (declare (ignorable unsigned))
364   #+ignore
365   (let ((array-def (gensym)))
366     `(let ((,array-def (list 'alien:array 'c-call:char ,size)))
367        (eval `(alien:cast (alien:make-alien ,,array-def)
368                           ,(if ,unsigned
369                                '(* (alien:unsigned 8))
370                              '(* (alien:signed 8)))))))
371
372   #+(or cmu scl)
373   `(alien:make-alien ,(if unsigned
374                              '(alien:unsigned 8)
375                              '(alien:signed 8))
376     ,size)
377
378   #+sbcl
379   `(sb-alien:make-alien ,(if unsigned
380                              '(sb-alien:unsigned 8)
381                              '(sb-alien:signed 8))
382     ,size)
383
384   #+lispworks
385   `(fli:allocate-foreign-object :type
386                                 ,(if unsigned
387                                      ''(:unsigned :char)
388                                    :char)
389                                 :nelems ,size)
390   #+allegro
391   `(ff:allocate-fobject :char :c ,size)
392   #+(or openmcl digitool)
393   `(new-ptr ,size)
394   )
395
396 (defun foreign-string-length (foreign-string)
397   #+allegro (ff:foreign-strlen foreign-string)
398   #-allegro
399   (loop
400      for size from 0
401      until (zerop (deref-array foreign-string '(:array :unsigned-char) size))
402      finally (return size)))
403
404
405 (defmacro with-foreign-string ((foreign-string lisp-string &optional encoding)
406                                &body body)
407   (let ((result (gensym))
408         (fe (gensym)))
409     `(let* ((,fe ,encoding)
410             (,foreign-string (convert-to-foreign-string ,lisp-string ,fe))
411             (,result (progn ,@body)))
412       (declare (dynamic-extent ,foreign-string))
413       (free-foreign-object ,foreign-string)
414       ,result)))
415
416 (defmacro with-foreign-strings (bindings &body body)
417   `(with-foreign-string ,(car bindings)
418     ,@(if (cdr bindings)
419           `((with-foreign-strings ,(cdr bindings) ,@body))
420           body)))
421
422 ;; Modified from CMUCL's source to handle non-null terminated strings
423 #+cmu
424 (defun cmucl-naturalize-cstring (sap &key length (null-terminated-p t))
425   (declare (type system:system-area-pointer sap))
426   (locally
427       (declare (optimize (speed 3) (safety 0)))
428     (let ((null-terminated-length
429            (when null-terminated-p
430              (loop
431                  for offset of-type fixnum upfrom 0
432                  until (zerop (system:sap-ref-8 sap offset))
433                  finally (return offset)))))
434       (if length
435           (if (and null-terminated-length
436                    (> (the fixnum length) (the fixnum null-terminated-length)))
437               (setq length null-terminated-length))
438         (setq length null-terminated-length)))
439     (let ((result (make-string length)))
440       (kernel:copy-from-system-area sap 0
441                                     result (* vm:vector-data-offset
442                                               vm:word-bits)
443                                     (* length vm:byte-bits))
444       result)))
445
446 #+scl
447 ;; kernel:copy-from-system-area doesn't work like it does on CMUCL or SBCL,
448 ;; so have to iteratively copy from sap
449 (defun cmucl-naturalize-cstring (sap &key length (null-terminated-p t))
450   (declare (type system:system-area-pointer sap))
451   (locally
452       (declare (optimize (speed 3) (safety 0)))
453     (let ((null-terminated-length
454            (when null-terminated-p
455              (loop
456                  for offset of-type fixnum upfrom 0
457                  until (zerop (system:sap-ref-8 sap offset))
458                  finally (return offset)))))
459       (if length
460           (if (and null-terminated-length
461                    (> (the fixnum length) (the fixnum null-terminated-length)))
462               (setq length null-terminated-length))
463         (setq length null-terminated-length)))
464     (let ((result (make-string length)))
465       (dotimes (i length)
466         (declare (type fixnum i))
467         (setf (char result i) (code-char (system:sap-ref-8 sap i))))
468       result)))
469
470 #+(and sbcl (not sb-unicode))
471 (defun sbcl-naturalize-cstring (sap &key length (null-terminated-p t))
472   (declare (type sb-sys:system-area-pointer sap)
473            (type (or null fixnum) length))
474   (locally
475    (declare (optimize (speed 3) (safety 0)))
476    (let ((null-terminated-length
477           (when null-terminated-p
478             (loop
479              for offset of-type fixnum upfrom 0
480              until (zerop (sb-sys:sap-ref-8 sap offset))
481              finally (return offset)))))
482      (if length
483          (if (and null-terminated-length
484                   (> (the fixnum length) (the fixnum null-terminated-length)))
485              (setq length null-terminated-length))
486        (setq length null-terminated-length)))
487    (let ((result (make-string length)))
488        (funcall *system-copy-fn* sap 0 result +system-copy-offset+
489                 (* length +system-copy-multiplier+))
490        result)))
491
492
493 (eval-when (:compile-toplevel :load-toplevel :execute)
494    (def-function "strlen"
495      ((str (* :unsigned-char)))
496      :returning :unsigned-int))
497
498 (def-type char-ptr-def (* :unsigned-char))
499
500 #+(or (and allegro (not ics)) (and lispworks (not lispworks5) (not lispworks6)))
501 (defun fast-native-to-string (s len)
502   (declare (optimize (speed 3) (space 0) (safety 0) (compilation-speed 0))
503            (type char-ptr-def s))
504   (let* ((len (or len (strlen s)))
505          (str (make-string len)))
506     (declare (fixnum len)
507              (type (simple-array #+lispworks base-char
508                                  #-lispworks (signed-byte 8) (*)) str))
509     (dotimes (i len str)
510       (setf (aref str i)
511         (uffi:deref-array s '(:array :char) i)))))
512
513 #+(or (and allegro ics) lispworks5 lispworks6)
514 (defun fast-native-to-string (s len)
515   (declare (optimize (speed 3) (space 0) (safety 0) (compilation-speed 0))
516            (type char-ptr-def s))
517   (let* ((len (or len (strlen s)))
518          (str (make-string len)))
519     (dotimes (i len str)
520       (setf (schar str i) (code-char (uffi:deref-array s '(:array :unsigned-byte) i))))))