|
110 | 110 | (defvar *decoder-prefers-alists* nil) |
111 | 111 | (defvar *decode-bin-as-string* nil) |
112 | 112 |
|
| 113 | +(defvar *extension-dispatchers* (make-hash-table :size #xFF) |
| 114 | + "Maps the extension type to a plist of functions whose keys are |
| 115 | + :matcher - predicate used by the ENCODE routine to determine who should encode the data (e.g. COMPLEXP) |
| 116 | + :encoder - function that encodes the data |
| 117 | + :decoder - function that decodes the data") |
113 | 118 | (defvar *extended-types* nil) |
114 | 119 | (defvar *lookup-table* nil) |
115 | 120 |
|
|
160 | 165 | (encode-hash data stream)) |
161 | 166 | ((symbolp data) |
162 | 167 | (encode-symbol data stream)) |
163 | | - ((and *extended-types* |
164 | | - (typep data 'extension-type) |
165 | | - (try-encode-ext-type data stream)) |
166 | | - t) |
| 168 | + ((and *extension-dispatchers* |
| 169 | + (dispatcher-for-extension-data data)) |
| 170 | + (funcall (encoder-for-extension-data data) data stream)) |
167 | 171 | (t (error |
168 | 172 | (format nil |
169 | 173 | "Cannot encode data ~a (maybe you should bind *extended-types*?)." data))))) |
|
305 | 309 | (or (<= 0 data (1- (expt 2 64))) |
306 | 310 | (<= (- (expt 2 63)) data (1- (expt 2 63))))) |
307 | 311 |
|
| 312 | +(defun encode-fixext (data ext type stream) |
| 313 | + (assert (<= #xd4 ext #xd8)) |
| 314 | + (write-byte ext stream) |
| 315 | + (write-byte type stream) |
| 316 | + (loop :for byte :in data :do (write-byte byte stream))) |
| 317 | + |
| 318 | +(defmacro fixext-encoder (ext size) |
| 319 | + `(progn |
| 320 | + (defun ,(mksymb 'encode-fixext- size) (data stream type) |
| 321 | + (let ((data (alexandria:ensure-list data))) |
| 322 | + (assert (<= (length data) ,size)) |
| 323 | + (encode-fixext data ,ext type stream))))) |
| 324 | + |
| 325 | +(fixext-encoder #xd4 1) |
| 326 | +(fixext-encoder #xd5 2) |
| 327 | +(fixext-encoder #xd6 4) |
| 328 | +(fixext-encoder #xd7 8) |
| 329 | +(fixext-encoder #xd8 16) |
| 330 | + |
| 331 | +(defmacro ext-encoder (ext size) |
| 332 | + `(progn |
| 333 | + (defun ,(mksymb 'encode-ext- size) (data stream type) |
| 334 | + (let ((data (alexandria:ensure-list data)) |
| 335 | + (length (length data))) |
| 336 | + (assert (<= length (1- (expt 2 ,size)))) |
| 337 | + (assert (<= #xc7 ,ext #xc9)) |
| 338 | + (write-byte ,ext stream) |
| 339 | + (store-big-endian (length data) stream ,size) |
| 340 | + (write-byte type stream) |
| 341 | + (loop :for byte in data :do (write-byte byte stream)))))) |
| 342 | + |
| 343 | +(ext-encoder #xc7 8) |
| 344 | +(ext-encoder #xc8 16) |
| 345 | +(ext-encoder #xc9 32) |
| 346 | + |
| 347 | +(defun register-extension-dispatcher (type matcher encoder decoder) |
| 348 | + ;; TODO What if those keys don't exist |
| 349 | + (setf (gethash type *extension-dispatchers*) |
| 350 | + (list :matcher matcher |
| 351 | + :encoder encoder |
| 352 | + :decoder decoder))) |
| 353 | + |
| 354 | +(defun dispatcher-for-extension-data (data) |
| 355 | + "Find the first dispatchers entry whose MATCHER predicate returns T for DATA" |
| 356 | + (find data (loop for v being the hash-value of *extension-dispatchers* collect v) |
| 357 | + :test (lambda (data hash-val) (funcall (getf hash-val :matcher) data)))) |
| 358 | + |
| 359 | +(defun encoder-for-extension-data (data) |
| 360 | + (getf (dispatcher-for-extension-data data) :encoder)) |
| 361 | + |
| 362 | +(defun decoder-for-extension-data (data) |
| 363 | + (getf (dispatcher-for-extension-data data) :decoder)) |
| 364 | + |
| 365 | +(defun extension-decode (type len stream) |
| 366 | + ;; TODO What if decoder doesn't exist? |
| 367 | + (funcall (getf (gethash type *extension-dispatchers*) :decoder) |
| 368 | + len stream)) |
| 369 | + |
| 370 | +(defun extension-encode (type data stream) |
| 371 | + (funcall (getf (gethash type *extension-dispatchers*) :encoder) |
| 372 | + data stream)) |
| 373 | + |
308 | 374 | (defun parse-big-endian (byte-array) |
309 | 375 | ;; TODO: do words at once? |
310 | 376 | (loop with result = 0 |
|
350 | 416 | (ub32->sb32 (load-big-endian stream 4))) |
351 | 417 | ((= #xd3 byte) |
352 | 418 | (ub64->sb64 (load-big-endian stream 8))) |
353 | | - ((<= #xd4 byte #xd8) ; fixext1: type, data |
| 419 | + ((<= #xd4 byte #xd8) |
354 | 420 | (let ((len (ash 1 (- byte #xd4)))) |
355 | | - (typed-data (read-byte stream) |
356 | | - (decode-byte-array len stream)))) |
| 421 | + (extension-decode (read-byte stream) |
| 422 | + len |
| 423 | + stream))) |
357 | 424 | ((= #xc7 byte) |
358 | 425 | (let ((len (read-byte stream))) |
359 | | - (typed-data (read-byte stream) |
360 | | - (decode-byte-array len stream)))) |
| 426 | + (extension-decode (read-byte stream) |
| 427 | + len |
| 428 | + stream))) |
361 | 429 | ((= #xc8 byte) |
362 | 430 | (let ((len (load-big-endian stream 2))) |
363 | | - (typed-data (read-byte stream) |
364 | | - (decode-byte-array len stream)))) |
| 431 | + (extension-decode (read-byte stream) |
| 432 | + len |
| 433 | + stream))) |
365 | 434 | ((= #xc9 byte) |
366 | 435 | (let ((len (load-big-endian stream 4))) |
367 | | - (typed-data (read-byte stream) |
368 | | - (decode-byte-array len stream)))) |
| 436 | + (extension-decode (read-byte stream) |
| 437 | + len |
| 438 | + stream))) |
369 | 439 | ((= #xc0 byte) |
370 | 440 | nil) |
371 | 441 | ((= #xc3 byte) |
|
473 | 543 | (let ((seq (make-array length :element-type '(mod 256)))) |
474 | 544 | (read-sequence seq stream) |
475 | 545 | (babel:octets-to-string seq))) |
476 | | - |
477 | | - |
478 | | - |
479 | | -;; How to get type-num for the types? |
480 | | -;; A class would have a :allocation :class ... |
481 | | -;; A pointer to the e-t-d would be longer than the int itself. |
482 | | -(defclass extension-type () |
483 | | - ((id :initform (error "need an ID") |
484 | | - :initarg :id |
485 | | - :reader extension-type-id |
486 | | - :writer (setf extension-type-id) |
487 | | - :type (or integer (array (unsigned-byte 8) *)))) |
488 | | - (:documentation |
489 | | - "Base type for Ext-Types.")) |
490 | | - |
491 | | -(defmethod print-object ((obj extension-type) stream) |
492 | | - (print-unreadable-object (obj stream :type T :identity T) |
493 | | - (format stream "~a" (extension-type-id obj)))) |
494 | | - |
495 | | - |
496 | | -(defclass extension-type-description () |
497 | | - #. (mapcar (lambda (d) |
498 | | - (destructuring-bind (name init &rest rest) d |
499 | | - `(,name :initform ,init |
500 | | - :initarg ,(intern (symbol-name name) :keyword) |
501 | | - :reader ,name |
502 | | - :writer (setf ,name) |
503 | | - ,@ rest))) |
504 | | - '((type-number nil :type (integer 0 127)) |
505 | | - (encode-with nil :type function) |
506 | | - (decode-with nil :type function) |
507 | | - (as-numeric nil :type (member t nil)) |
508 | | - (reg-class nil) |
509 | | - ))) |
510 | | - |
511 | | -(defmethod print-object ((obj extension-type-description) stream) |
512 | | - (print-unreadable-object (obj stream :type T :identity T) |
513 | | - (format stream "~a ~d" |
514 | | - (class-name (reg-class obj)) |
515 | | - (type-number obj)))) |
516 | | - |
517 | | - |
518 | | -(defun symbol-to-extension-type (num sym decode-as) |
519 | | - (assert (member decode-as '(:numeric :byte-array))) |
520 | | - (let ((num? (eq decode-as :numeric))) |
521 | | - (unless (find-class sym nil) |
522 | | - (closer-mop:ensure-class |
523 | | - sym |
524 | | - :direct-superclasses '(extension-type))) |
525 | | - (flet |
526 | | - ((maybe-cache (obj id) |
527 | | - (if *lookup-table* |
528 | | - (or |
529 | | - (lookup-table-find num id) |
530 | | - (lookup-table-insert num id obj)) |
531 | | - obj))) |
532 | | - (make-instance 'extension-type-description |
533 | | - :type-number num |
534 | | - :reg-class (find-class sym) |
535 | | - :encode-with (lambda (obj) |
536 | | - ;; TODO: better use EXTENSION-TYPE-ID? |
537 | | - (let ((id (slot-value obj 'id))) |
538 | | - ;; store outgoing objects... |
539 | | - (maybe-cache obj id) |
540 | | - id)) |
541 | | - :decode-with (lambda (id) |
542 | | - ;; TODO: (if num? ( ... ) x)? |
543 | | - (let ((obj (make-instance sym |
544 | | - 'id id))) |
545 | | - ;; store incoming objects... |
546 | | - ;; TODO: what if that object already exists? |
547 | | - (or |
548 | | - (maybe-cache obj id) |
549 | | - obj))) |
550 | | - :as-numeric num?)))) |
551 | | - |
552 | | - |
553 | | -(defun typed-data (type-num bytes) |
554 | | - (let ((ext-type (find type-num *extended-types* |
555 | | - :test #'eql |
556 | | - :key #'type-number))) |
557 | | - ;; TODO: better throw or error? |
558 | | - (assert ext-type) |
559 | | - (funcall (decode-with ext-type) |
560 | | - (if (as-numeric ext-type) |
561 | | - (parse-big-endian bytes) |
562 | | - bytes)))) |
563 | | - |
564 | | -(defun try-encode-ext-type (obj stream) |
565 | | - (let ((ext-type (find (class-of obj) *extended-types* |
566 | | - :test #'eq |
567 | | - :key #'reg-class))) |
568 | | - ;; doesn't run ENCODE-WITH function?! |
569 | | - (when ext-type |
570 | | - (let* ((id (funcall (encode-with ext-type) obj)) |
571 | | - (bytes (if (numberp id) |
572 | | - (flexi-streams:with-output-to-sequence (s) |
573 | | - (encode-integer id s)) |
574 | | - id)) |
575 | | - (len (length bytes))) |
576 | | - ;; TODO: in theory the ID might be longer than 256 bytes... |
577 | | - ;; (encode-sequence-length bytes stream #xc7 0 #xc8 #xc9) |
578 | | - ;; but we need the type inbetween. |
579 | | - (assert (<= 0 len #xff)) |
580 | | - (write-byte #xc7 stream) |
581 | | - (write-byte len stream) |
582 | | - (write-byte (type-number ext-type) stream) |
583 | | - (write-sequence bytes stream)) |
584 | | - T))) |
585 | | - |
586 | | - |
587 | | -(defun define-extension-types (args) |
588 | | - "This function defines types for the MessagePack extension type system |
589 | | - (#xD4 to #xD8, and #xC7 to #xC9), and returns a list of them |
590 | | - that can be bound to *EXTENSION-TYPES*. |
591 | | - 128 different types can be available simultaneously at any one time. |
592 | | -
|
593 | | - This function takes integers, flags, and/or closures as arguments; |
594 | | - these get used as items for the next arguments. |
595 | | - * Integers define which type number to use next. |
596 | | - * Flags for decoding: |
597 | | - :BYTE-ARRAY - return the bytes as array. Default. |
598 | | - :NUMERIC - return value in DATA as a number. Only for fixextN. |
599 | | - * A symbol associates the current type number to this type; |
600 | | - this type should be derived from MESSAGEPACK-EXT-TYPE, as |
601 | | - to have a correct MESSAGEPACK:ID slot. |
602 | | -
|
603 | | - Example: |
604 | | - (defvar *my-extension-types* |
605 | | - (define-extension-types :numeric |
606 | | - 5 'buffer 'block |
607 | | - 8 'cursor)) |
608 | | - Eg., the type 6 would then return (MAKE-BLOCK 'ID <content>)." |
609 | | - (loop with type-num = 0 |
610 | | - with decode-as = :byte-array |
611 | | - ; with encode |
612 | | - for el in args |
613 | | - append (cond |
614 | | - ((numberp el) |
615 | | - (if (<= 0 el 127) |
616 | | - (setf type-num el) |
617 | | - (error "Integer ~a out of range." el)) |
618 | | - nil) |
619 | | - ((member el '(:byte-array :numeric)) |
620 | | - (setf decode-as el) |
621 | | - nil) |
622 | | - ((keywordp el) |
623 | | - (error "Keywords ~s not in use." el)) |
624 | | - ((symbolp el) |
625 | | - (prog1 |
626 | | - (list (symbol-to-extension-type type-num el decode-as)) |
627 | | - (incf type-num))) |
628 | | - (T |
629 | | - (error "~s not understood." el)) |
630 | | - ))) |
631 | | - |
632 | | - |
633 | | -(defun make-lookup-table () |
634 | | - "Returns something that can be used for *LOOKUP-TABLE*." |
635 | | - (make-hash-table :test #'equalp)) |
636 | | - |
637 | | -(defun lookup-table-insert (type id obj) |
638 | | - (setf (gethash (cons type id) *lookup-table*) obj)) |
639 | | - |
640 | | -(defun lookup-table-find (type id) |
641 | | - (gethash (cons type id) *lookup-table*)) |
0 commit comments