blob: 50da203a7b66d609439161175243cc3df7b6a267 [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))
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010073 .map(DeltaSuperblock::byteSwappedIfBigEndian);
74
75 private DeltaSuperblock byteSwappedIfBigEndian() {
76 // Fix up the endianness of the 'entries' and 'fallbacks' fields, which have
77 // unspecified byte order.
78 var endiannessMetadatum = metadata().fields().get("ostree.endianness");
79 if (endiannessMetadatum != null
80 && endiannessMetadatum.value() instanceof Byte endiannessByte
81 && endiannessByte == (byte) 'B') {
82 return byteSwapped();
83 } else {
84 return this;
85 }
86 }
Matthias Andreas Benkarda8514a32021-12-30 21:01:48 +010087
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}