blob: b8143af51a91dc2845a27920cde537dc3b6aea11 [file] [log] [blame]
Matthias Andreas Benkardb5d657a2022-02-03 21:14:30 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01005package eu.mulk.jgvariant.ostree;
6
7import eu.mulk.jgvariant.core.Decoder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01008import java.io.ByteArrayOutputStream;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01009import java.nio.ByteBuffer;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010010import java.nio.ByteOrder;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010011import java.util.ArrayList;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010012import java.util.List;
13
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010014/**
15 * A static delta.
16 *
17 * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT}
18 *
19 * @param metadata arbitrary user-supplied metadata.
20 * @param timestamp UNIX epoch seconds of when the commit was done.
21 * @param fromChecksum a (possibly {@link Checksum#isEmpty()}) reference to the starting commit.
22 * @param toChecksum a (non-{@link Checksum#isEmpty()}) reference to the end commit.
23 * @param commit the commit metadata of the end commit.
24 * @param dependencies a list of other {@link DeltaSuperblock}s that need to be applied before this
25 * one.
26 * @param entries a list of metadata on the {@link DeltaPartPayload}s that make up the delta.
27 * @param fallbacks a list of objects included in the delta as plain files that have to be fetched
28 * separately.
29 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010030public record DeltaSuperblock(
31 Metadata metadata,
32 long timestamp,
33 Checksum fromChecksum,
34 Checksum toChecksum,
35 Commit commit,
36 List<DeltaName> dependencies,
37 List<DeltaMetaEntry> entries,
38 List<DeltaFallback> fallbacks) {
39
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010040 /**
41 * A specifier for another static delta.
42 *
43 * <p>Used to specify {@link DeltaSuperblock#dependencies()}.
44 *
45 * @param fromChecksum the {@link DeltaSuperblock#fromChecksum()} of the referenced delta.
46 * @param toChecksum the {@link DeltaSuperblock#toChecksum()} of the referenced delta.
47 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010048 public record DeltaName(Checksum fromChecksum, Checksum toChecksum) {
49
50 private static final Decoder<DeltaName> DECODER =
51 Decoder.ofStructure(DeltaName.class, Checksum.decoder(), Checksum.decoder());
52
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010053 /**
54 * Acquires a {@link Decoder} for the enclosing type.
55 *
56 * @return a possibly shared {@link Decoder}.
57 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010058 public static Decoder<DeltaName> decoder() {
59 return DECODER;
60 }
61 }
62
63 private static final Decoder<DeltaSuperblock> DECODER =
64 Decoder.ofStructure(
Matthias Andreas Benkarda8514a32021-12-30 21:01:48 +010065 DeltaSuperblock.class,
66 Metadata.decoder(),
67 Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
68 Checksum.decoder(),
69 Checksum.decoder(),
70 Commit.decoder(),
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010071 Decoder.ofByteArray()
72 .map(
73 DeltaSuperblock::parseDeltaNameList, DeltaSuperblock::serializeDeltaNameList),
Matthias Andreas Benkarda8514a32021-12-30 21:01:48 +010074 Decoder.ofArray(DeltaMetaEntry.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN),
75 Decoder.ofArray(DeltaFallback.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN))
Matthias Andreas Benkardc442ebe2023-12-10 17:58:38 +010076 .map(DeltaSuperblock::byteSwappedIfBigEndian, DeltaSuperblock::byteSwappedIfBigEndian);
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010077
78 private DeltaSuperblock byteSwappedIfBigEndian() {
79 // Fix up the endianness of the 'entries' and 'fallbacks' fields, which have
80 // unspecified byte order.
81 var endiannessMetadatum = metadata().fields().get("ostree.endianness");
82 if (endiannessMetadatum != null
83 && endiannessMetadatum.value() instanceof Byte endiannessByte
84 && endiannessByte == (byte) 'B') {
85 return byteSwapped();
86 } else {
87 return this;
88 }
89 }
Matthias Andreas Benkarda8514a32021-12-30 21:01:48 +010090
91 private DeltaSuperblock byteSwapped() {
92 return new DeltaSuperblock(
93 metadata,
94 timestamp,
95 fromChecksum,
96 toChecksum,
97 commit,
98 dependencies,
99 entries.stream().map(DeltaMetaEntry::byteSwapped).toList(),
100 fallbacks.stream().map(DeltaFallback::byteSwapped).toList());
101 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100102
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100103 private static byte[] serializeDeltaNameList(List<DeltaName> deltaNames) {
104 var output = new ByteArrayOutputStream();
105
106 for (var deltaName : deltaNames) {
107 output.writeBytes(deltaName.fromChecksum().byteString().bytes());
108 output.writeBytes(deltaName.toChecksum().byteString().bytes());
109 }
110
111 return output.toByteArray();
112 }
113
Matthias Andreas Benkard05114642021-12-29 21:51:29 +0100114 private static List<DeltaName> parseDeltaNameList(byte[] bytes) {
115 var byteBuffer = ByteBuffer.wrap(bytes);
116 List<DeltaName> deltaNames = new ArrayList<>();
117
118 while (byteBuffer.hasRemaining()) {
119 var fromChecksum = Checksum.readFrom(byteBuffer);
120 var toChecksum = Checksum.readFrom(byteBuffer);
121 deltaNames.add(new DeltaName(fromChecksum, toChecksum));
122 }
123
124 return deltaNames;
125 }
126
127 /**
128 * Acquires a {@link Decoder} for the enclosing type.
129 *
130 * @return a possibly shared {@link Decoder}.
131 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100132 public static Decoder<DeltaSuperblock> decoder() {
133 return DECODER;
134 }
135}