git.fiddlerwoaroof.com
delta.lisp
d468a84b
 (in-package :fwoar.cl-git)
 
489818ad
 (defclass delta (git-object)
d468a84b
   ((%repository :initarg :repository :reader repository)
    (%base :initarg :base :reader base)
2733d76a
    (%commands :initarg :commands :reader commands)
    (%src-size :initarg :src-size :reader src-size)
9d5bb297
    (%delta-size :initarg :delta-size :reader delta-size))
   (:documentation
    "The base type for deltified git objects"))
d468a84b
 
 (defclass+ ofs-delta (delta)
   ())
 
 (defclass+ ref-delta (delta)
9d5bb297
   ()
   (:documentation "TODO: mostly unimplemented/untested"))
d468a84b
 
2733d76a
 (defun make-ofs-delta (base commands repository src-size delta-size)
   (fw.lu:new 'ofs-delta base commands repository src-size delta-size))
d468a84b
 (defun make-ref-delta (base commands repository)
   (fw.lu:new 'ofs-delta base commands repository))
 
f5b39481
 (defun int->bit-vector (n)
   (let* ((integer-length (integer-length n))
          (bv-size (* 8 (ceiling integer-length 8)))
          (bv (make-array bv-size :element-type 'bit)))
     (loop :for ix :below integer-length
           :do (setf (aref bv (- bv-size 1 ix))
                     (if (logbitp ix n)
                         1
                         0)))
     bv))
 
 (defun bit-vector->int (bv)
   (let ((bv-size (array-total-size bv)))
     (loop :for ix :from (1- bv-size) :downto 0
           :for n :from 0
           :unless (zerop (aref bv ix))
             :sum (expt 2 n))))
 
55e71580
 (defun obj-to-type (obj)
   (etypecase obj
     (git-commit :commit)
     (git-tree :tree)
     (blob :blob)))
 
9d5bb297
 (defun trace-bases (pack delta)
6df34ee9
   (assert (typep delta 'delta))
   (let* ((offset (second (base delta)))
          (o (extract-object-at-pos pack
                                    offset
                                    (make-instance 'git-ref
                                                   :hash "00000000"
                                                   :repo nil)))
          (obj (serapeum:assocdr :object-data o))
          (raw (serapeum:assocdr :raw-data o)))
     (if (typep obj 'delta)
55e71580
         (multiple-value-bind (next base-type) (trace-bases pack obj)
           (values (apply-commands next
                                   (commands delta))
                   base-type))
6df34ee9
         (let ((base (apply-commands raw
                                     (commands delta))))
           (length base)
55e71580
           (values base
                   (obj-to-type obj))))))
 
 (defun resolve-delta (ref maybe-delta)
   (typecase maybe-delta
     (delta (multiple-value-bind (raw-data type) (trace-bases (packed-ref-pack ref)
                                                              maybe-delta)
              (-extract-object-of-type type
                                       raw-data
                                       (ref-repo ref)
                                       :hash (ref-hash ref))))
     (t maybe-delta)))
6df34ee9
 
 (defun get-bases (pack delta)
9d5bb297
   (if (typep delta 'delta)
       (let* ((offset (second (base delta)))
              (o (extract-object-at-pos pack
                                        offset
                                        (make-instance 'git-ref
                                                       :hash "00000000"
                                                       :repo nil)))
6df34ee9
              (obj (serapeum:assocdr :object-data o)))
         (cons delta (get-bases pack obj)))
       (list delta)))
b27750d1
 
d468a84b
 (defun partition-commands (data)
   (let ((idx 0))
     (labels ((advance ()
b27750d1
                (if (>= idx (length data))
                    (progn (incf idx)
                           0)
                    (prog1 (elt data idx)
                      (incf idx))))
d468a84b
              (get-command ()
f5b39481
                (let* ((bv (int->bit-vector (elt data idx)))
d468a84b
                       (discriminator (elt bv 0))
                       (insts (subseq bv 1)))
                  (incf idx)
                  (if (= 1 discriminator)
b27750d1
                      (expand-copy
                       (list :copy
                             insts
                             (coerce (loop repeat (count 1 insts) collect (advance))
                                     '(vector (unsigned-byte 8)))))
d468a84b
                      (list :add
b27750d1
                            (coerce (loop repeat (bit-vector->int insts)
d468a84b
                                          collect (advance))
9d5bb297
                                    '(vector (unsigned-byte 8)))))))
              (expand-copy (copy)
                (destructuring-bind (command layout numbers) copy
                  (let* ((next-idx 0)
                         (parts (map '(vector (unsigned-byte 8))
                                     (lambda (layout-bit)
                                       (if (= layout-bit 1)
                                           (prog1 (elt numbers next-idx)
                                             (incf next-idx))
                                           0))
                                     (reverse layout))))
                    (list command
                          (fwoar.bin-parser:le->int (subseq parts 0 4))
                          (fwoar.bin-parser:le->int (subseq parts 4)))))))
d468a84b
       (loop while (< idx (length data))
             collect (get-command)))))
 
9d5bb297
 (defun apply-commands (base commands)
   (flexi-streams:with-output-to-sequence (s)
     (flet ((do-copy (offset cnt)
6df34ee9
              #+(or)
              (format t "DOING :COPY ~d ~d~%" offset cnt)
9d5bb297
              (write-sequence (subseq base offset (+ offset cnt))
                              s))
            (do-add (data)
6df34ee9
              #+(or)
              (format t "DOING :ADD ~d~%" (length data))
9d5bb297
              (write-sequence data s)))
       (loop for (command . args) in commands
             when (eql command :copy) do
               (apply #'do-copy args)
             when (eql command :add) do
               (apply #'do-add args)))))
b27750d1
 
6df34ee9
 (defun get-ofs-delta-offset-streaming (buf)
   (let* ((idx 0))
     (flet ((advance ()
              (read-byte buf)))
       (loop
         for c = (advance)
         for ofs = (logand c 127) then (+ (ash (1+ ofs)
                                               7)
                                          (logand c 127))
         while (> (logand c 128) 0)
         finally
            (return (values (- ofs) idx))))))
 
b27750d1
 (defun get-ofs-delta-offset (buf)
a1d069b5
   (let* ((idx 0))
b27750d1
     (flet ((advance ()
              (prog1 (elt buf idx)
                (incf idx))))
a1d069b5
       (loop
         for c = (advance)
         for ofs = (logand c 127) then (+ (ash (1+ ofs)
                                               7)
                                          (logand c 127))
         while (> (logand c 128) 0)
         finally
            (return (values (- ofs) idx))))))
2733d76a
 
 (defun decode-size (buf)
   (let ((parts ()))
     (loop for raw across buf
           for bits = (int->bit-vector raw)
           for morep = (= (elt bits 0) 1)
           do (push (subseq bits 1) parts)
           while morep)
     (let ((result (make-array (* 7 (length parts))
                               :element-type 'bit)))
       (loop for x from 0 by 7
             for part in parts
             do
                (replace result part :start1 x))
       (values (bit-vector->int result)
               (length parts)))))
b27750d1
 
6df34ee9
 (defmethod -extract-object-of-type ((type (eql :ofs-delta)) s repository &key offset-from packfile base)
   (multiple-value-bind (src-size consumed-1) (decode-size s)
     (multiple-value-bind (delta-size consumed-2) (decode-size (subseq s
                                                                       consumed-1))
       (make-ofs-delta (list packfile
                             (+ offset-from base))
                       (partition-commands (subseq s
                                                   (+ consumed-1
                                                      consumed-2)))
                       repository
                       src-size
                       delta-size))))
 
d468a84b
 (defmethod -extract-object-of-type ((type (eql :ref-delta)) s repository &key offset-from)
   (make-ref-delta offset-from
                   (partition-commands s)
                   repository))