blob: 10b16619c1555e1d38696c397dbd4cb81099d4eb [file] [log] [blame]
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01001package eu.mulk.jgvariant.ostree;
2
3import eu.mulk.jgvariant.core.Decoder;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01004import java.nio.ByteBuffer;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01005import java.nio.ByteOrder;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01006import java.util.ArrayList;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01007import java.util.List;
8
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01009/**
10 * A static delta.
11 *
12 * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT}
13 *
14 * @param metadata arbitrary user-supplied metadata.
15 * @param timestamp UNIX epoch seconds of when the commit was done.
16 * @param fromChecksum a (possibly {@link Checksum#isEmpty()}) reference to the starting commit.
17 * @param toChecksum a (non-{@link Checksum#isEmpty()}) reference to the end commit.
18 * @param commit the commit metadata of the end commit.
19 * @param dependencies a list of other {@link DeltaSuperblock}s that need to be applied before this
20 * one.
21 * @param entries a list of metadata on the {@link DeltaPartPayload}s that make up the delta.
22 * @param fallbacks a list of objects included in the delta as plain files that have to be fetched
23 * separately.
24 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010025public record DeltaSuperblock(
26 Metadata metadata,
27 long timestamp,
28 Checksum fromChecksum,
29 Checksum toChecksum,
30 Commit commit,
31 List<DeltaName> dependencies,
32 List<DeltaMetaEntry> entries,
33 List<DeltaFallback> fallbacks) {
34
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010035 /**
36 * A specifier for another static delta.
37 *
38 * <p>Used to specify {@link DeltaSuperblock#dependencies()}.
39 *
40 * @param fromChecksum the {@link DeltaSuperblock#fromChecksum()} of the referenced delta.
41 * @param toChecksum the {@link DeltaSuperblock#toChecksum()} of the referenced delta.
42 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010043 public record DeltaName(Checksum fromChecksum, Checksum toChecksum) {
44
45 private static final Decoder<DeltaName> DECODER =
46 Decoder.ofStructure(DeltaName.class, Checksum.decoder(), Checksum.decoder());
47
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010048 /**
49 * Acquires a {@link Decoder} for the enclosing type.
50 *
51 * @return a possibly shared {@link Decoder}.
52 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010053 public static Decoder<DeltaName> decoder() {
54 return DECODER;
55 }
56 }
57
58 private static final Decoder<DeltaSuperblock> DECODER =
59 Decoder.ofStructure(
Matthias Andreas Benkarda8514a32021-12-30 21:01:48 +010060 DeltaSuperblock.class,
61 Metadata.decoder(),
62 Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
63 Checksum.decoder(),
64 Checksum.decoder(),
65 Commit.decoder(),
66 Decoder.ofByteArray().map(DeltaSuperblock::parseDeltaNameList),
67 Decoder.ofArray(DeltaMetaEntry.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN),
68 Decoder.ofArray(DeltaFallback.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN))
69 .map(
70 deltaSuperblock -> {
71 // Fix up the endianness of the 'entries' and 'fallbacks' fields, which have
72 // unspecified byte order.
73 var endiannessMetadatum =
74 deltaSuperblock.metadata().fields().get("ostree.endianness");
75 if (endiannessMetadatum != null
76 && endiannessMetadatum.value() instanceof Byte endiannessByte
77 && endiannessByte == (byte) 'B') {
78 return deltaSuperblock.byteSwapped();
79 } else {
80 return deltaSuperblock;
81 }
82 });
83
84 private DeltaSuperblock byteSwapped() {
85 return new DeltaSuperblock(
86 metadata,
87 timestamp,
88 fromChecksum,
89 toChecksum,
90 commit,
91 dependencies,
92 entries.stream().map(DeltaMetaEntry::byteSwapped).toList(),
93 fallbacks.stream().map(DeltaFallback::byteSwapped).toList());
94 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010095
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010096 private static List<DeltaName> parseDeltaNameList(byte[] bytes) {
97 var byteBuffer = ByteBuffer.wrap(bytes);
98 List<DeltaName> deltaNames = new ArrayList<>();
99
100 while (byteBuffer.hasRemaining()) {
101 var fromChecksum = Checksum.readFrom(byteBuffer);
102 var toChecksum = Checksum.readFrom(byteBuffer);
103 deltaNames.add(new DeltaName(fromChecksum, toChecksum));
104 }
105
106 return deltaNames;
107 }
108
109 /**
110 * Acquires a {@link Decoder} for the enclosing type.
111 *
112 * @return a possibly shared {@link Decoder}.
113 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100114 public static Decoder<DeltaSuperblock> decoder() {
115 return DECODER;
116 }
117}