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