(in-package :fwoar.cl-git) (defun find-object-in-pack-files (repo id) (dolist (pack-file (pack-files repo)) (multiple-value-bind (pack mid) (find-pack-containing pack-file id) (when pack (return-from find-object-in-pack-files (values pack mid)))))) (defun edges-in-fanout (toc s sha) (let* ((fanout-offset (getf toc :fanout))) (file-position s (+ fanout-offset (* 4 (1- (elt sha 0))))) (destructuring-bind ((_ . cur) (__ . next)) (fwoar.bin-parser:extract '((cur 4 fwoar.bin-parser:be->int) (next 4 fwoar.bin-parser:be->int)) s) (declare (ignore _ __)) (values cur next)))) (defun find-sha-between-terms (toc s start end sha) (unless (>= start end) (let* ((sha-offset (getf toc :shas)) (mid (floor (+ start end) 2))) (file-position s (+ sha-offset (* 20 mid))) (let ((sha-at-mid (read-bytes 20 'fwoar.bin-parser:byte-array-to-hex-string s))) (cond ((string< sha sha-at-mid) (find-sha-between-terms toc s start mid sha)) ((string> sha sha-at-mid) (find-sha-between-terms toc s (1+ mid) end sha)) (t mid)))))) (defun find-pack-containing (pack-file id) (with-open-file (s (index-file pack-file) :element-type '(unsigned-byte 8)) (let ((binary-sha (ironclad:hex-string-to-byte-array id)) (toc (idx-toc s))) (multiple-value-bind (_ end) (edges-in-fanout toc s binary-sha) (declare (ignore _)) (let ((midpoint (find-sha-between-terms toc s 0 end id))) (and midpoint (values pack-file midpoint))))))) (defun extract-object-from-pack (pack obj-number) (with-open-file (s (index-file pack) :element-type '(unsigned-byte 8)) (with-open-file (p (pack-file pack) :element-type '(unsigned-byte 8)) (let* ((toc (idx-toc s)) (offset-offset (getf toc :4-byte-offsets))) (file-position s (+ offset-offset (* 4 obj-number))) (let ((object-offset-in-pack (read-bytes 4 'fwoar.bin-parser:be->int s))) (file-position p object-offset-in-pack) (read-object-from-pack p)))))) (defun seek-to-object-in-pack (idx-stream pack-stream obj-number) (let* ((toc (idx-toc idx-stream)) (offset-offset (getf toc :4-byte-offsets))) (file-position idx-stream (+ offset-offset (* 4 obj-number))) (let ((object-offset-in-pack (read-bytes 4 'fwoar.bin-parser:be->int idx-stream))) (file-position pack-stream object-offset-in-pack)))) (defun extract-object-metadata-from-pack (pack obj-number) (with-open-file (s (index-file pack) :element-type '(unsigned-byte 8)) (with-open-file (p (pack-file pack) :element-type '(unsigned-byte 8)) (seek-to-object-in-pack s p obj-number) (read-object-metadata-from-pack p)))) (defun extract-loose-object (repo id) (with-open-file (s (object repo id) :element-type '(unsigned-byte 8)) (chipz:decompress nil (chipz:make-dstate 'chipz:zlib) s))) (defun extract-object (repo id) (if (object repo id) (extract-loose-object repo id) (data-lens.lenses:view *object-data-lens* (multiple-value-call 'extract-object-from-pack (find-object-in-pack-files (root repo) id))))) (defun turn-read-object-to-string (object) (data-lens.lenses:over *object-data-lens* 'babel:octets-to-string object)) (defgeneric branches (repository) (:method ((repository repository)) (get-local-branches (root repository)))) (defgeneric branch (repository name) (:method ((repository repository) name) (second (find name (get-local-branches (root repository)) :test 'equal :key 'car)))) (defgeneric object (repository id) (:method ((repository repository) id) (car (uiop:directory* (merge-pathnames (loose-object-path (serapeum:concat id "*")) (root repository)))))) (defun fanout-table (s) (coerce (alexandria:assoc-value (fwoar.bin-parser:extract '((head 4) (version 4) (fanout-table #.(* 4 256) batch-4)) s) 'fanout-table) 'vector)) (defun get-object-size (bytes) (let ((first (elt bytes 0)) (rest (subseq bytes 1))) (logior (ash (fwoar.bin-parser:be->int rest) 4) (logand first 15)))) (defun get-object-type (bytes) (let ((first (elt bytes 0))) (ldb (byte 3 4) first))) (defun get-shas-before (fanout-table first-sha-byte s) (let ((num-before (elt fanout-table first-sha-byte)) (num-total (alexandria:last-elt fanout-table))) (values (fwoar.bin-parser:extract (list (list 'shas (* 20 num-before) '->sha-string)) s) (- num-total num-before)))) (defun advance-past-crcs (obj-count s) (file-position s (+ (file-position s) (* 4 obj-count)))) (defun object-offset (object-number s) (file-position s (+ (file-position s) (* (1- object-number) 4))) (fwoar.bin-parser:extract '((offset 4 fwoar.bin-parser:be->int)) s)) (defun idx-toc (idx-stream) (let* ((object-count (progn (file-position idx-stream 1028) (let ((buf (make-array 4))) (read-sequence buf idx-stream) (fwoar.bin-parser:be->int buf)))) (signature 0) (version 4) (fanout 8) (shas (+ fanout (* 4 256))) (packed-crcs (+ shas (* 20 object-count))) (4-byte-offsets (+ packed-crcs (* 4 object-count))) (8-byte-offsets-pro (+ 4-byte-offsets (* object-count 4))) (pack-sha (- (file-length idx-stream) 40)) (8-byte-offsets (when (/= 8-byte-offsets-pro pack-sha) 8-byte-offsets-pro)) (idx-sha (- (file-length idx-stream) 20))) (values (sym->plist signature version fanout shas packed-crcs 4-byte-offsets 8-byte-offsets pack-sha idx-sha) object-count))) (defun collect-data (idx-toc s num) (let ((sha-idx (getf idx-toc :shas)) (crc-idx (getf idx-toc :packed-crcs)) (4-byte-offsets-idx (getf idx-toc :4-byte-offsets)) (8-byte-offsets-idx (getf idx-toc :8-byte-offsets))) (declare (ignore 8-byte-offsets-idx)) (values num (progn (file-position s (+ sha-idx (* num 20))) (read-bytes 20 'fwoar.bin-parser:byte-array-to-hex-string s)) (progn (file-position s (+ crc-idx (* num 4))) (read-bytes 4 'identity s)) (progn (file-position s (+ 4-byte-offsets-idx (* num 4))) (read-bytes 4 'fwoar.bin-parser:be->int s))))) (defun read-object-metadata-from-pack (s) (let* ((metadata (fwoar.bin-parser:extract-high s)) (type (get-object-type metadata)) (size (get-object-size metadata))) (values (cons :type (object-type->sym type)) (cons :decompressed-size size)))) (defun read-object-from-pack (s) (let* ((metadata (fwoar.bin-parser:extract-high s)) (type (get-object-type metadata)) (size (get-object-size metadata)) (object-data (chipz:decompress nil (chipz:make-dstate 'chipz:zlib) s))) (list (cons :type (object-type->sym type)) (cons :decompressed-size size) (cons :object-data object-data) (cons :raw-data object-data)))) (defun get-first-commits-from-pack (idx pack n) (let ((toc (idx-toc idx)) (result ())) (dotimes (i n (reverse result)) (multiple-value-bind (_ sha __ offset) (collect-data toc idx i) (declare (ignore _ __)) (file-position pack offset) (push `((:sha . ,sha) ,@(multiple-value-list (read-object-metadata-from-pack pack)) (:offset . ,offset)) result)))))