Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
commit.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
type t = { entries : Entry.t list; seed_tag : Cstruct.t; last_tag : Cstruct.t; start_crc : Optint.t; (* either the default CRC or the CRC of the revision count (for the first commit in a block) *) } let sizeof_crc = 4 let last_tag t = t.last_tag let seed_tag t = t.seed_tag let entries t = t.entries let create starting_xor_tag preceding_crc = { entries = []; last_tag = starting_xor_tag; seed_tag = starting_xor_tag; start_crc = preceding_crc; } let addv t entries = (* unfortunately we need to serialize all the entries in order to get the crc *) let new_last_tag,_ = Entry.to_cstructv ~starting_xor_tag:t.last_tag entries in { entries = t.entries @ entries; seed_tag = t.last_tag; last_tag = new_last_tag; start_crc = t.start_crc; } let commit_after {last_tag; _} entries = let seed_tag = last_tag in let new_last_tag, _cs = Entry.to_cstructv ~starting_xor_tag:seed_tag entries in (* the crc for any entry that's after another one (any non-first entry on a block) * doesn't depend on the revision count, so its calculation is more straightforward *) let start_crc = Checkseum.Crc32.default in (* we get the final result whether we do a lognot on this or, uh, not, * which indicates to me that maybe we're not actually using this value *) { entries; seed_tag; last_tag = new_last_tag; start_crc; } let of_entries_filter_crc starting_xor_tag preceding_crc entries = let entries = List.filter (fun (entry : Entry.t) -> (* we don't want to include the CRC tag in the read-back entry list, * since we calculate that on write in our own code. *) let (tag, _data) = entry in match fst @@ tag.Tag.type3 with | Tag.LFS_TYPE_CRC -> false | _ -> true ) entries in addv (create starting_xor_tag preceding_crc) entries (** [into_cstruct cs t] writes [t] to [cs] starting at offset 0. * It returns the raw (not XOR'd with the tag before it) value * of the last tag of the commit as serialized (i.e., the CRC tag), * for use in writing any commits that may follow [t]. * Unlike other modules the corresponding `to_cstruct` function is * not provided, because the caller is expected to be writing into * a larger buffer as part of a block write of a set of commits. * *) let into_cstruct ~filter_hardtail ~starting_offset ~program_block_size ~starting_xor_tag ~next_commit_valid cs t = (* we would like to be sure that we don't write any hardtail entries, * since the block level will be handling that *) let entries = if filter_hardtail then List.filter (fun (t, _d) -> not @@ Tag.is_hardtail t) t.entries else t.entries in let entries_length = Entry.lenv_less_hardtail entries in let unpadded_length = starting_offset + entries_length + Tag.size + sizeof_crc in let overhang = unpadded_length mod program_block_size in let padding = (program_block_size - overhang) mod program_block_size in (* for a lot of future calculation, we'll need to know where writing the (non-CRC) entries * into the buffer completed. *) let crc_tag_pointer, last_tag = Entry.into_cstructv ~starting_xor_tag cs entries in let crc_pointer = crc_tag_pointer + Tag.size in let crc_chunk = if next_commit_valid then 0x00 else 0x01 in let crc_tag = Tag.({ valid = true; type3 = Tag.LFS_TYPE_CRC, crc_chunk; id = 0x3ff; length = sizeof_crc + padding; }) in let entry_region = Cstruct.sub cs 0 crc_tag_pointer in let tag_region = Cstruct.sub cs crc_tag_pointer Tag.size in let crc_region = Cstruct.sub cs crc_pointer sizeof_crc in let padding_region = Cstruct.sub cs (crc_pointer + sizeof_crc) padding in (* since the crc includes the tag for the crc itself, we need to write the tag before * we can calculate the crc value for the buffer *) Tag.into_cstruct ~xor_tag_with:last_tag tag_region crc_tag; let seed_crc = Checkseum.Crc32.digest_bigstring (Cstruct.to_bigarray entry_region) 0 crc_tag_pointer t.start_crc in (* the crc in t is the crc of all the entries, so we can use that input to a crc calculation of the tag *) let crc_with_tag = Checkseum.Crc32.digest_bigstring (Cstruct.to_bigarray tag_region) 0 Tag.size seed_crc |> Optint.(logand @@ of_unsigned_int32 0xffffffffl) |> Optint.lognot |> Optint.(logand @@ of_unsigned_int32 0xffffffffl) in Cstruct.LE.set_uint32 crc_region 0 Optint.(to_unsigned_int32 crc_with_tag); (* set the padding bytes to an obvious value *) if padding <= 0 then () else Cstruct.memset padding_region 0xff; let raw_tag = Tag.to_cstruct_raw crc_tag in (unpadded_length + padding - starting_offset, raw_tag) let rec of_cstructv ~starting_offset:_ ~program_block_size ~starting_xor_tag ~preceding_crc cs = (* we don't have a good way to know how many valid * entries there are (since we filter out the CRC tags) , * so we have to keep trying for the whole block :/ *) let entries, last_tag, read = Entry.of_cstructv ~starting_xor_tag cs in match entries with | [] -> [] | entries -> (* `read` includes padding from CRC tags, so all reads after the first one should * be aligned with the program block size *) if read >= Cstruct.length cs then (of_entries_filter_crc starting_xor_tag preceding_crc entries) :: [] else begin let next_commit = Cstruct.shift cs read in (* only the first commit ever has a nonzero starting offset, so all our recursive calls should set it to 0 *) let commit = of_entries_filter_crc starting_xor_tag preceding_crc entries in commit :: of_cstructv ~preceding_crc:Checkseum.Crc32.default ~starting_offset:0 ~starting_xor_tag:last_tag ~program_block_size next_commit end