+ "There is no Protobuf message having the type ~S" type)
+ (object-size object message visited)))
+
+;; 'visited' is used to cache object sizes
+;; The default method uses metadata from the protobuf "schema" for the message
+(defmethod object-size (object (message protobuf-message) &optional visited)
+ (let ((size 0))
+ (declare (type fixnum size))
+ (macrolet ((read-slot (object slot reader)
+ ;; Don't do a boundp check, we assume the object is fully populated
+ ;; Unpopulated slots should be "nullable" and will contain nil when empty
+ `(if ,reader
+ (funcall ,reader ,object)
+ (slot-value ,object ,slot))))
+ (labels ((do-field (object trace field)
+ ;; We don't do cycle detection here
+ ;; If the client needs it, he can define his own 'object-size'
+ ;; method to clean things up first
+ (let* ((type (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
+ (slot (proto-value field))
+ (reader (proto-reader field))
+ msg)
+ (when (or slot reader)
+ (cond ((eq (proto-required field) :repeated)
+ (cond ((and (proto-packed field) (packed-type-p type))
+ (let ((tag (make-tag type (proto-index field))))
+ (iincf size (packed-size (read-slot object slot reader) type tag))))
+ ((keywordp type)
+ (let ((tag (make-tag type (proto-index field))))
+ (doseq (v (read-slot object slot reader))
+ (iincf size (prim-size v type tag)))))
+ ((typep (setq msg (and type (or (find-message trace type)
+ (find-enum trace type)
+ (find-type-alias trace type))))
+ 'protobuf-message)
+ (if (eq (proto-message-type msg) :group)
+ (doseq (v (if slot (read-slot object slot reader) (list object)))
+ (let ((tag1 (make-tag $wire-type-start-group (proto-index field)))
+ (tag2 (make-tag $wire-type-end-group (proto-index field))))
+ (iincf size (length32 tag1))
+ (dolist (f (proto-fields msg))
+ (do-field v msg f))
+ (iincf size (length32 tag2))))
+ (doseq (v (if slot (read-slot object slot reader) (list object)))
+ (let ((tag (make-tag $wire-type-string (proto-index field)))
+ (len (or (cached-object-size v visited)
+ (object-size v msg visited))))
+ (iincf size (length32 tag))
+ (iincf size (length32 len))
+ (dolist (f (proto-fields msg))
+ (do-field v msg f))))))
+ ((typep msg 'protobuf-enum)
+ (let ((tag (make-tag $wire-type-varint (proto-index field))))
+ (if (proto-packed field)
+ (iincf size (packed-enum-size (read-slot object slot reader) type tag))
+ (doseq (v (read-slot object slot reader))
+ (iincf size (enum-size v (proto-values msg) tag))))))
+ ((typep msg 'protobuf-type-alias)
+ (let* ((type (proto-proto-type msg))
+ (tag (make-tag type (proto-index field))))
+ (doseq (v (read-slot object slot reader))
+ (let ((v (funcall (proto-serializer msg) v)))
+ (iincf size (prim-size v type tag))))))
+ (t
+ (undefined-field-type "While computing the size of ~S,"
+ object type field))))
+ (t
+ (cond ((eq type :bool)
+ (let ((v (cond ((or (eq (proto-required field) :required)
+ (null slot))
+ (read-slot object slot reader))
+ ((slot-boundp object slot)
+ (read-slot object slot reader))
+ (t :unbound))))
+ (unless (eq v :unbound)
+ (let ((tag (make-tag :bool (proto-index field))))
+ (iincf size (prim-size v type tag))))))
+ ((keywordp type)
+ (let ((v (read-slot object slot reader)))
+ (when (and v (not (equal v (proto-default field))))
+ (let ((tag (make-tag type (proto-index field))))
+ (iincf size (prim-size v type tag))))))
+ ((typep (setq msg (and type (or (find-message trace type)
+ (find-enum trace type)
+ (find-type-alias trace type))))
+ 'protobuf-message)
+ (let ((v (if slot (read-slot object slot reader) object)))
+ (when v
+ (if (eq (proto-message-type msg) :group)
+ (let ((tag1 (make-tag $wire-type-start-group (proto-index field)))
+ (tag2 (make-tag $wire-type-end-group (proto-index field))))
+ (iincf size (length32 tag1))
+ (dolist (f (proto-fields msg))
+ (do-field v msg f))
+ (iincf size (length32 tag2)))
+ (let ((tag (make-tag $wire-type-string (proto-index field)))
+ (len (or (cached-object-size v visited)
+ (object-size v msg visited))))
+ (iincf size (length32 tag))
+ (iincf size (length32 len))
+ (dolist (f (proto-fields msg))
+ (do-field v msg f)))))))
+ ((typep msg 'protobuf-enum)
+ (let ((v (read-slot object slot reader)))
+ (when (and v (not (eql v (proto-default field))))
+ (let ((tag (make-tag $wire-type-varint (proto-index field))))
+ (iincf size (enum-size (read-slot object slot reader) (proto-values msg) tag))))))
+ ((typep msg 'protobuf-type-alias)
+ (let ((v (read-slot object slot reader)))
+ (when v
+ (let* ((v (funcall (proto-serializer msg) v))
+ (type (proto-proto-type msg))
+ (tag (make-tag type (proto-index field))))
+ (iincf size (prim-size v type tag))))))
+ (t
+ (undefined-field-type "While computing the size of ~S,"
+ object type field)))))))))
+ (declare (dynamic-extent #'do-field))
+ (dolist (field (proto-fields message))
+ (do-field object message field))
+ (setf (cached-object-size object visited) size) ;cache the size
+ size))))
+
+\f
+;;; Compile-time generation of serializers
+;;; Type-checking is done at the top-level methods specialized on 'symbol',
+;;; so we turn off all type checking at the level of these functions
+
+;; Note well: keep this in sync with the main 'serialize-object' method above
+(defun generate-serializer (message)
+ "Generate a 'serialize-object' method for the given message."
+ (with-gensyms (vobj vbuf vidx vval vclass)
+ (when (null (proto-fields message))
+ (return-from generate-serializer
+ `(defmethod serialize-object
+ (,vobj (,vclass (eql ,message)) ,vbuf &optional (,vidx 0) visited)
+ (declare #.$optimize-serialization)
+ (declare (ignorable ,vobj ,vclass visited)
+ (type (simple-array (unsigned-byte 8)) ,vbuf)
+ (type fixnum ,vidx))
+ (values ,vbuf ,vidx))))
+ (with-collectors ((serializers collect-serializer))
+ (dolist (field (proto-fields message))
+ (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
+ (msg (and class (not (keywordp class))
+ (or (find-message message class)
+ (find-enum message class)
+ (find-type-alias message class))))
+ (reader (cond ((proto-reader field)
+ `(,(proto-reader field) ,vobj))
+ ((proto-value field)
+ `(slot-value ,vobj ',(proto-value field)))))
+ (index (proto-index field)))
+ (when reader
+ (cond ((eq (proto-required field) :repeated)
+ (let* ((vectorp (vector-field-p field))
+ (iterator (if vectorp 'dovector 'dolist)))
+ (cond ((and (proto-packed field) (packed-type-p class))
+ (collect-serializer
+ (let ((tag (make-tag class index)))
+ `(setq ,vidx (serialize-packed ,reader ,class ,tag ,vbuf ,vidx
+ ,vectorp)))))
+ ((keywordp class)
+ (collect-serializer
+ (let ((tag (make-tag class index)))
+ `(,iterator (,vval ,reader)
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx))))))
+ ((typep msg 'protobuf-message)
+ (collect-serializer
+ (if (eq (proto-message-type msg) :group)
+ (let ((tag1 (make-tag $wire-type-start-group index))
+ (tag2 (make-tag $wire-type-end-group index)))
+ `(,iterator (,vval ,reader)
+ (let ((len (cached-object-size ,vval visited)))
+ (setq ,vidx (encode-uint32 ,tag1 ,vbuf ,vidx))
+ (serialize-object ,vval ,msg ,vbuf ,vidx visited)
+ (iincf ,vidx len)
+ (setq ,vidx (encode-uint32 ,tag2 ,vbuf ,vidx)))))
+ (let ((tag (make-tag $wire-type-string index)))
+ `(,iterator (,vval ,reader)
+ (let ((len (cached-object-size ,vval visited)))
+ (setq ,vidx (encode-uint32 ,tag ,vbuf ,vidx))
+ (setq ,vidx (encode-uint32 len ,vbuf ,vidx))
+ (serialize-object ,vval ,msg ,vbuf ,vidx visited)
+ (iincf ,vidx len)))))))
+ ((typep msg 'protobuf-enum)
+ (collect-serializer
+ (let ((tag (make-tag $wire-type-varint index)))
+ (if (proto-packed field)
+ `(setq ,vidx (serialize-packed-enum ,reader '(,@(proto-values msg)) ,tag ,vbuf ,vidx
+ ,vectorp))
+
+ `(,iterator (,vval ,reader)
+ (setq ,vidx (serialize-enum ,vval '(,@(proto-values msg)) ,tag ,vbuf ,vidx)))))))
+ ((typep msg 'protobuf-type-alias)
+ (collect-serializer
+ (let* ((class (proto-proto-type msg))
+ (tag (make-tag class (proto-index field))))
+ `(,iterator (,vval ,reader)
+ (let ((,vval (funcall #',(proto-serializer msg) ,vval)))
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx)))))))
+ (t
+ (undefined-field-type "While generating 'serialize-object' for ~S,"
+ message class field)))))
+ (t
+ (cond ((keywordp class)
+ (collect-serializer
+ (let ((tag (make-tag class index)))
+ (if (eq class :bool)
+ (if (or (eq (proto-required field) :required)
+ (null (proto-value field)))
+ `(let ((,vval ,reader))
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx)))
+ `(let ((,vval (cond ((slot-boundp ,vobj ',(proto-value field))
+ ,reader)
+ (t :unbound))))
+ (unless (eq ,vval :unbound)
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx)))))
+ (if (empty-default-p field)
+ `(let ((,vval ,reader))
+ (when ,vval
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx))))
+ `(let ((,vval ,reader))
+ (when (and ,vval (not (equal ,vval ',(proto-default field))))
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx)))))))))
+ ((typep msg 'protobuf-message)
+ (collect-serializer
+ (if (eq (proto-message-type msg) :group)
+ (let ((tag1 (make-tag $wire-type-start-group index))
+ (tag2 (make-tag $wire-type-end-group index)))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (let ((len (cached-object-size ,vval visited)))
+ (setq ,vidx (encode-uint32 ,tag1 ,vbuf ,vidx))
+ (serialize-object ,vval ,msg ,vbuf ,vidx visited)
+ (iincf ,vidx len)
+ (setq ,vidx (encode-uint32 ,tag2 ,vbuf ,vidx))))))
+ (let ((tag (make-tag $wire-type-string index)))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (let ((len (cached-object-size ,vval visited)))
+ (setq ,vidx (encode-uint32 ,tag ,vbuf ,vidx))
+ (setq ,vidx (encode-uint32 len ,vbuf ,vidx))
+ (serialize-object ,vval ,msg ,vbuf ,vidx visited)
+ (iincf ,vidx len))))))))
+ ((typep msg 'protobuf-enum)
+ (collect-serializer
+ (let ((tag (make-tag $wire-type-varint index)))
+ (if (empty-default-p field)
+ `(let ((,vval ,reader))
+ (when ,vval
+ (setq ,vidx (serialize-enum ,vval '(,@(proto-values msg)) ,tag ,vbuf ,vidx))))
+ `(let ((,vval ,reader))
+ (when (and ,vval (not (eql ,vval ',(proto-default field))))
+ (setq ,vidx (serialize-enum ,vval '(,@(proto-values msg)) ,tag ,vbuf ,vidx))))))))
+ ((typep msg 'protobuf-type-alias)
+ (collect-serializer
+ (let* ((class (proto-proto-type msg))
+ (tag (make-tag class (proto-index field))))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (let ((,vval (funcall #',(proto-serializer msg) ,vval)))
+ (setq ,vidx (serialize-prim ,vval ,class ,tag ,vbuf ,vidx))))))))
+ (t
+ (undefined-field-type "While generating 'serialize-object' for ~S,"
+ message class field))))))))
+ `(defmethod serialize-object
+ (,vobj (,vclass (eql ,message)) ,vbuf &optional (,vidx 0) visited)
+ (declare #.$optimize-serialization)
+ (declare (ignorable visited)
+ (type (simple-array (unsigned-byte 8)) ,vbuf)
+ (type fixnum ,vidx))
+ ,@serializers
+ (values ,vbuf ,vidx)))))
+
+;; Note well: keep this in sync with the main 'deserialize-object' method above
+(defun generate-deserializer (message)
+ "Generate a 'deserialize-object' method for the given message."
+ (with-gensyms (vclass vbuf vidx vlen vendtag vobj vval)
+ (when (null (proto-fields message))
+ (return-from generate-deserializer
+ `(defmethod deserialize-object
+ ((,vclass (eql ,message)) ,vbuf &optional ,vidx ,vlen (,vendtag 0))
+ (declare #.$optimize-serialization)
+ (declare (ignorable ,vclass ,vbuf ,vlen ,vendtag)
+ (type (simple-array (unsigned-byte 8)) ,vbuf))
+ (let ((,vidx (or ,vidx 0)))
+ (declare (type fixnum ,vidx))
+ (let ((,vobj (make-instance ',(or (proto-alias-for message) (proto-class message)))))
+ (values ,vobj ,vidx))))))
+ (with-collectors ((deserializers collect-deserializer)
+ ;; For tracking repeated slots that will need to be reversed
+ (rslots collect-rslot))
+ (flet ((read-slot (object field)
+ (cond ((proto-reader field)
+ `(,(proto-reader field) ,object))
+ ((proto-value field)
+ `(slot-value ,object ',(proto-value field)))))
+ (write-slot (object field value)
+ (cond ((proto-writer field)
+ `(,(proto-writer field) ,object ,value))
+ ((proto-value field)
+ `(setf (slot-value ,object ',(proto-value field)) ,value)))))
+ (dolist (field (proto-fields message))
+ (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
+ (msg (and class (not (keywordp class))
+ (or (find-message message class)
+ (find-enum message class)
+ (find-type-alias message class))))
+ (index (proto-index field)))
+ (cond ((eq (proto-required field) :repeated)
+ (cond ((and (proto-packed field) (packed-type-p class))
+ (collect-deserializer
+ `((,(make-tag class index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-packed ,class ,vbuf ,vidx)
+ (setq ,vidx idx)
+ ,@(when (vector-field-p field)
+ `((setq ,vval (make-array (length ,vval)
+ :fill-pointer t :adjustable t
+ :initial-contents ,vval))))
+ ,(write-slot vobj field vval)))))
+ ((keywordp class)
+ (let ((temp (gensym (string (proto-value field)))))
+ (collect-rslot (list field temp))
+ (collect-deserializer
+ `((,(make-tag class index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-prim ,class ,vbuf ,vidx)
+ (setq ,vidx idx)
+ (push ,vval ,temp))))))
+ ((typep msg 'protobuf-message)
+ (let ((temp (gensym (string (proto-value field)))))
+ (collect-rslot (list field temp))
+ (collect-deserializer
+ (if (eq (proto-message-type msg) :group)
+ `((,(make-tag $wire-type-start-group index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-object ,msg ,vbuf ,vidx ,vlen
+ ,(make-tag $wire-type-end-group index))
+ (setq ,vidx idx)
+ (push ,vval ,temp)))
+ `((,(make-tag $wire-type-string index))
+ (multiple-value-bind (len idx)
+ (decode-uint32 ,vbuf ,vidx)
+ (setq ,vidx idx)
+ (multiple-value-bind (,vval idx)
+ (deserialize-object ,msg ,vbuf ,vidx (i+ ,vidx len) 0)
+ (setq ,vidx idx)
+ (push ,vval ,temp))))))))
+ ((typep msg 'protobuf-enum)
+ (if (proto-packed field)
+ (collect-deserializer
+ `((,(make-tag $wire-type-varint index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-packed-enum '(,@(proto-values msg)) ,vbuf ,vidx)
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval))))
+ (let ((temp (gensym (string (proto-value field)))))
+ (collect-rslot (list field temp))
+ (collect-deserializer
+ `((,(make-tag $wire-type-varint index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-enum '(,@(proto-values msg)) ,vbuf ,vidx)
+ (setq ,vidx idx)
+ (push ,vval ,temp)))))))
+ ((typep msg 'protobuf-type-alias)
+ (let ((class (proto-proto-type msg))
+ (temp (gensym (string (proto-value field)))))
+ (collect-rslot (list field temp))
+ (collect-deserializer
+ `((,(make-tag class index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-prim ,class ,vbuf ,vidx)
+ (setq ,vidx idx)
+ (push (funcall #',(proto-deserializer msg) ,vval) ,temp))))))
+ (t
+ (undefined-field-type "While generating 'deserialize-object' for ~S,"
+ message class field))))
+ (t
+ (cond ((keywordp class)
+ (collect-deserializer
+ `((,(make-tag class index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-prim ,class ,vbuf ,vidx)
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval)))))
+ ((typep msg 'protobuf-message)
+ (collect-deserializer
+ (if (eq (proto-message-type msg) :group)
+ `((,(make-tag $wire-type-start-group index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-object ,msg ,vbuf ,vidx ,vlen
+ ,(make-tag $wire-type-end-group index))
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval)))
+ `((,(make-tag $wire-type-string index))
+ (multiple-value-bind (len idx)
+ (decode-uint32 ,vbuf ,vidx)
+ (setq ,vidx idx)
+ (multiple-value-bind (,vval idx)
+ (deserialize-object ,msg ,vbuf ,vidx (i+ ,vidx len) 0)
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval)))))))
+ ((typep msg 'protobuf-enum)
+ (collect-deserializer
+ `((,(make-tag $wire-type-varint index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-enum '(,@(proto-values msg)) ,vbuf ,vidx)
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval)))))
+ ((typep msg 'protobuf-type-alias)
+ (let ((class (proto-proto-type msg)))
+ (collect-deserializer
+ `((,(make-tag class index))
+ (multiple-value-bind (,vval idx)
+ (deserialize-prim ,class ,vbuf ,vidx)
+ (let ((,vval (funcall #',(proto-deserializer msg) ,vval)))
+ (setq ,vidx idx)
+ ,(write-slot vobj field vval)))))))
+ (t
+ (undefined-field-type "While generating 'deserialize-object' for ~S,"
+ message class field))))))))
+ (let* ((rslots (delete-duplicates rslots :key #'first))
+ (rfields (mapcar #'first rslots))
+ (rtemps (mapcar #'second rslots)))
+ `(defmethod deserialize-object
+ ((,vclass (eql ,message)) ,vbuf &optional ,vidx ,vlen (,vendtag 0))
+ (declare #.$optimize-serialization)
+ (declare (type (simple-array (unsigned-byte 8)) ,vbuf))
+ (let ((,vidx (or ,vidx 0))
+ (,vlen (or ,vlen (length ,vbuf))))
+ (declare (type fixnum ,vidx ,vlen))
+ (let ((,vobj (make-instance ',(or (proto-alias-for message) (proto-class message))))
+ ;; Bind the temporary variables that hold repeated slots
+ ,@rtemps)
+ (loop
+ (multiple-value-bind (tag idx)
+ (if (i< ,vidx ,vlen) (decode-uint32 ,vbuf ,vidx) (values 0 ,vidx))
+ (setq ,vidx idx)
+ (when (i= tag ,vendtag)
+ ;; Set the (un)reversed values of the repeated slots
+ ,@(loop for field in rfields
+ for temp in rtemps
+ as slot = (proto-value field)
+ as writer = (proto-writer field)
+ collect (cond ((vector-field-p field)
+ (if writer
+ `(funcall ,writer ,vobj (make-array (length ,temp)
+ :fill-pointer t :adjustable t
+ :initial-contents (nreverse ,temp)))
+ `(setf (slot-value ,vobj ',slot) (make-array (length ,temp)
+ :fill-pointer t :adjustable t
+ :initial-contents (nreverse ,temp)))))
+ (t
+ (if writer
+ `(funcall ,writer ,vobj (nreverse ,temp))
+ `(setf (slot-value ,vobj ',slot) (nreverse ,temp))))))
+ (return-from deserialize-object
+ (values ,vobj ,vidx)))
+ (case tag
+ ,@deserializers
+ (otherwise
+ (setq ,vidx (skip-element ,vbuf ,vidx tag)))))))))))))
+
+;; Note well: keep this in sync with the main 'object-size' method above
+(defun generate-object-size (message)
+ "Generate an 'object-size' method for the given message."
+ (with-gensyms (vobj vsize vval vclass)
+ (when (null (proto-fields message))
+ (return-from generate-object-size
+ `(defmethod object-size
+ (,vobj (,vclass (eql ,message)) &optional visited)
+ (declare #.$optimize-serialization)
+ (setf (cached-object-size ,vobj visited) 0)
+ 0)))
+ (with-collectors ((sizers collect-sizer))
+ (dolist (field (proto-fields message))
+ (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
+ (msg (and class (not (keywordp class))
+ (or (find-message message class)
+ (find-enum message class)
+ (find-type-alias message class))))
+ (reader (cond ((proto-reader field)
+ `(,(proto-reader field) ,vobj))
+ ((proto-value field)
+ `(slot-value ,vobj ',(proto-value field)))))
+ (index (proto-index field)))
+ (when reader
+ (cond ((eq (proto-required field) :repeated)
+ (let* ((vectorp (vector-field-p field))
+ (iterator (if vectorp 'dovector 'dolist)))
+ (cond ((and (proto-packed field) (packed-type-p class))
+ (collect-sizer
+ (let ((tag (make-tag class index)))
+ `(iincf ,vsize (packed-size ,reader ,class ,tag ,vectorp)))))
+ ((keywordp class)
+ (collect-sizer
+ (let ((tag (make-tag class index)))
+ `(,iterator (,vval ,reader)
+ (iincf ,vsize (prim-size ,vval ,class ,tag))))))
+ ((typep msg 'protobuf-message)
+ (collect-sizer
+ (if (eq (proto-message-type msg) :group)
+ (let ((tag1 (make-tag $wire-type-start-group index))
+ (tag2 (make-tag $wire-type-end-group index)))
+ `(,iterator (,vval ,reader)
+ (let ((len (or (cached-object-size ,vval visited)
+ (object-size ,vval ,msg visited))))
+ (iincf ,vsize (length32 ,tag1))
+ (iincf ,vsize len)
+ (iincf ,vsize ,tag2))))
+ (let ((tag (make-tag $wire-type-string index)))
+ `(,iterator (,vval ,reader)
+ (let ((len (or (cached-object-size ,vval visited)
+ (object-size ,vval ,msg visited))))
+ (iincf ,vsize (length32 ,tag))
+ (iincf ,vsize (length32 len))
+ (iincf ,vsize len)))))))
+ ((typep msg 'protobuf-enum)
+ (let ((tag (make-tag $wire-type-varint index)))
+ (collect-sizer
+ (if (proto-packed field)
+ `(iincf ,vsize (packed-enum-size ,reader '(,@(proto-values msg)) ,tag))
+ `(,iterator (,vval ,reader)
+ (iincf ,vsize (enum-size ,vval '(,@(proto-values msg)) ,tag)))))))
+ ((typep msg 'protobuf-type-alias)
+ (collect-sizer
+ (let* ((class (proto-proto-type msg))
+ (tag (make-tag class index)))
+ `(,iterator (,vval ,reader)
+ (let ((,vval (funcall #',(proto-serializer msg) ,vval)))
+ (iincf ,vsize (prim-size ,vval ,class ,tag)))))))
+ (t
+ (undefined-field-type "While generating 'object-size' for ~S,"
+ message class field)))))
+ (t
+ (cond ((keywordp class)
+ (let ((tag (make-tag class index)))
+ (collect-sizer
+ (if (eq class :bool)
+ (if (or (eq (proto-required field) :required)
+ (null (proto-value field)))
+ `(let ((,vval ,reader))
+ (declare (ignorable ,vval))
+ (iincf ,vsize (prim-size ,vval ,class ,tag)))
+ `(let ((,vval (cond ((slot-boundp ,vobj ',(proto-value field))
+ ,reader)
+ (t :unbound))))
+ (unless (eq ,vval :unbound)
+ (iincf ,vsize (prim-size ,vval ,class ,tag)))))
+ (if (empty-default-p field)
+ `(let ((,vval ,reader))
+ (when ,vval
+ (iincf ,vsize (prim-size ,vval ,class ,tag))))
+ `(let ((,vval ,reader))
+ (when (and ,vval (not (equal ,vval ',(proto-default field))))
+ (iincf ,vsize (prim-size ,vval ,class ,tag)))))))))
+ ((typep msg 'protobuf-message)
+ (collect-sizer
+ (if (eq (proto-message-type msg) :group)
+ (let ((tag1 (make-tag $wire-type-start-group index))
+ (tag2 (make-tag $wire-type-end-group index)))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (let ((len (or (cached-object-size ,vval visited)
+ (object-size ,vval ,msg visited))))
+ (iincf ,vsize (length32 ,tag1))
+ (iincf ,vsize len)
+ (iincf ,vsize (length32 ,tag2))))))
+ (let ((tag (make-tag $wire-type-string index)))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (let ((len (or (cached-object-size ,vval visited)
+ (object-size ,vval ,msg visited))))
+ (iincf ,vsize (length32 ,tag))
+ (iincf ,vsize (length32 len))
+ (iincf ,vsize len))))))))
+ ((typep msg 'protobuf-enum)
+ (let ((tag (make-tag $wire-type-varint index)))
+ (collect-sizer
+ (if (empty-default-p field)
+ `(let ((,vval ,reader))
+ (when ,vval
+ (iincf ,vsize (enum-size ,vval '(,@(proto-values msg)) ,tag))))
+ `(let ((,vval ,reader))
+ (when (and ,vval (not (eql ,vval ',(proto-default field))))
+ (iincf ,vsize (enum-size ,vval '(,@(proto-values msg)) ,tag))))))))
+ ((typep msg 'protobuf-type-alias)
+ (collect-sizer
+ (let* ((class (proto-proto-type msg))
+ (tag (make-tag class index)))
+ `(let ((,vval ,reader))
+ (when ,vval
+ (iincf ,vsize (prim-size (funcall #',(proto-serializer msg) ,vval)
+ ,class ,tag)))))))
+ (t
+ (undefined-field-type "While generating 'object-size' for ~S,"
+ message class field))))))))
+ `(defmethod object-size
+ (,vobj (,vclass (eql ,message)) &optional visited)
+ (declare #.$optimize-serialization)
+ (declare (ignorable visited))
+ (let ((,vsize 0))
+ (declare (type fixnum ,vsize))
+ ,@sizers
+ (setf (cached-object-size ,vobj visited) ,vsize)
+ ,vsize)))))