diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2022-01-23 18:10:03 +0100 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2022-01-23 18:10:03 +0100 |
commit | c7aa2b6a54064157dc167002c850cd403c3b1656 (patch) | |
tree | ab255bf08d9666102d5ac58a6614d6abda711f8f | |
parent | e14bd1b88c708276d267d0e05c68a7714f7d5afc (diff) |
Document repository layout, add modified Base64 ByteString encoding.
Change-Id: I564db0e346346b608fa11527590e264c694fedaf
6 files changed, 348 insertions, 6 deletions
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java index cf6e99a..bb8cbb6 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java @@ -2,6 +2,7 @@ package eu.mulk.jgvariant.ostree; import eu.mulk.jgvariant.core.Decoder; import java.util.Arrays; +import java.util.Base64; import java.util.HexFormat; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -62,6 +63,34 @@ public record ByteString(byte[] bytes) { } /** + * Converts the contained byte array into modified Base64 (with {@code "/"} replaced with {@code + * "-"}). + * + * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * <p>Useful for printing. + * + * @return a modified Base64 representation of the bytes making up this checksum. + */ + public String modifiedBase64() { + return Base64.getEncoder().withoutPadding().encodeToString(bytes).replace('/', '_'); + } + + /** + * Parses a modified Base64 string into a {@link Checksum}. + * + * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * @param mbase64 a hex string. + * @return a {@link Checksum} corresponding to the given modified Base64 string. + */ + public static ByteString ofModifiedBase64(String mbase64) { + return new ByteString(Base64.getDecoder().decode(mbase64.replace('_', '/'))); + } + + /** * Returns the number of bytes in the byte string. * * @return the number of bytes in the byte string. diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java index ce5f9b0..94a728c 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java @@ -76,6 +76,34 @@ public record Checksum(ByteString byteString) { } /** + * Converts the contained byte array into modified Base64 (with {@code "/"} replaced with {@code + * "-"}). + * + * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * <p>Useful for printing. + * + * @return a modified Base64 representation of the bytes making up this checksum. + */ + public String modifiedBase64() { + return byteString.modifiedBase64(); + } + + /** + * Parses a modified Base64 string into a {@link Checksum}. + * + * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * @param mbase64 a hex string. + * @return a {@link Checksum} corresponding to the given modified Base64 string. + */ + public static Checksum ofModifiedBase64(String mbase64) { + return new Checksum(ByteString.ofModifiedBase64(mbase64)); + } + + /** * Reads a Checksum for a {@link ByteBuffer}. * * @param byteBuffer the byte buffer to read from. diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java index 28371fc..721d857 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java @@ -24,12 +24,56 @@ import org.apiguardian.api.API; */ @API(status = STABLE) public enum ObjectType { + + /** + * A regular file. + * + * <p>File ending: {@code .file} + */ FILE((byte) 1, "file"), + + /** + * A serialized {@link DirTree} object. + * + * <p>File ending: {@code .dirtree} + */ DIR_TREE((byte) 2, "dirtree"), + + /** + * A serialized {@link DirMeta} object. + * + * <p>File ending: {@code .dirmeta} + */ DIR_META((byte) 3, "dirmeta"), + + /** + * A serialized {@link Commit} object. + * + * <p>File ending: {@code .commit} + */ COMMIT((byte) 4, "commit"), + + /** + * A tombstone file standing in for a commit that was deleted. + * + * <p>File ending: {@code .commit-tombstone} + */ TOMBSTONE_COMMIT((byte) 5, "commit-tombstone"), + + /** + * Detached metadata for a {@link Commit}. + * + * <p>Often goes together with a {@link #TOMBSTONE_COMMIT}. + * + * <p>File ending: {@code .commitmeta} + */ COMMIT_META((byte) 6, "commitmeta"), + + /** + * A symlink to a {@link #FILE} that lives somewhere else. + * + * <p>File ending: {@code .payload-link} + */ PAYLOAD_LINK((byte) 7, "payload-link"); private final byte byteValue; diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java index c6a61f1..028f4cd 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java @@ -2,6 +2,183 @@ * Provides record classes describing the elements of <a * href="https://ostreedev.github.io/ostree/">OSTree</a> repositories and factory methods to create * {@link eu.mulk.jgvariant.core.Decoder} instances for them. + * + * <h2>OStree repository structure</h2> + * + * <p>An OSTree repository has the following layout: + * + * <table> + * <caption>OSTree repository layout</caption> + * + * <tr> + * <td>{@code config}</td> + * <td> + * <p> + * A plain text file that contains various settings. Among other things, this defines + * the <a href="https://ostreedev.github.io/ostree/formats/#the-archive-format">archive + * format</a> of the repository and whether files are compressed ({@code mode=archive-z2}) + * or plain ({@code mode=bare}, {@code mode=bare-user}). + * </p> + * </td> + * </tr> + * + * <tr> + * <td>{@code summary}</td> + * <td> + * <p> + * A {@link eu.mulk.jgvariant.ostree.Summary} object containing information on what is + * available under {@code refs/}, {@code deltas/}, and {@code delta-indexes/}. + * </p> + * + * <p>This file may or may not exist and, if it exists, may or may not be up to date.</p> + * </td> + * </tr> + * + * <tr> + * <td>{@code refs/heads/{name...}}</td> + * <td>Plain-text files containing {@link eu.mulk.jgvariant.ostree.Checksum}s in hex format + * (see {@link eu.mulk.jgvariant.ostree.Checksum#ofHex}) referring to + * {@link eu.mulk.jgvariant.ostree.Commit} objects. See below for the layout of the + * {@code objects/} directory that this refers to.</td> + * </tr> + * + * <tr> + * <td>{@code objects/{checksumHead}/{checksumRest}.{fileExtension}}</td> + * <td> + * <p> + * Objects of various types as described by {@link eu.mulk.jgvariant.ostree.ObjectType} + * and keyed by {@link eu.mulk.jgvariant.ostree.Checksum}. + * </p> + * + * <p> + * Among other things, this is where you find {@link eu.mulk.jgvariant.ostree.Commit} + * ({@code .commit)}, {@link eu.mulk.jgvariant.ostree.DirTree} ({@code .dirtree}), + * and {@link eu.mulk.jgvariant.ostree.DirMeta} ({@code .dirmeta}) objects as well as + * plain ({@code .file}) or compressed files ({@code .filez}). + * </p> + * + * <p> + * Static delta information is not stored here, but in the {@code deltas/} and + * {@code delta-indexes/} directories (if available). + * </p> + * + * <p> + * The individual parts of the file path are defined as follows: + * </p> + * + * <dl> + * <dt>{@code {checksumHead}} + * <dd> + * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#hex()} + * <dt>{@code {checksumRest}} + * <dd> + * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#hex()} starting from the + * 3rd character + * <dt>{@code {fileExtension}} + * <dd> + * the {@link eu.mulk.jgvariant.ostree.ObjectType#fileExtension()} of the object type + * </dl> + * </td> + * </tr> + * + * <tr id="delta-superblock"> + * <td>{@code deltas/{targetChecksumMbase64Head}/{targetChecksumMbase64Rest}/superblock}</td> + * <td> + * <p> + * A {@link eu.mulk.jgvariant.ostree.DeltaSuperblock} to get from nothing (an empty commit) + * to the commit named by the checksum encoded in the path. + * </p> + * + * <p> + * The individual parts of the file path are defined as follows: + * </p> + * + * <dl> + * <dt>{@code {checksumHead}} + * <dd> + * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + * <dt>{@code {checksumRest}} + * <dd> + * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + * </dl> + * </td> + * </tr> + * + * <tr> + * <td>{@code deltas/{targetChecksumMbase64Head}/{targetChecksumMbase64Rest}/{digit...}}</td> + * <td> + * <p> + * A {@link eu.mulk.jgvariant.ostree.DeltaPartPayload} belonging to a delta that goes from + * nothing (an empty commit) to the commit named by the checksum encoded in the path. + * </p> + * + * <p> + * The individual parts of the file path are defined as follows: + * </p> + * + * <dl> + * <dt>{@code {checksumHead}} + * <dd> + * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + * <dt>{@code {checksumRest}} + * <dd> + * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + * </dl> + * </td> + * </tr> + * + * <tr> + * <td>{@code deltas/{sourceChecksumMbase64Head}/{sourceChecksumMbase64Rest}-{targetChecksumMbase64}/superblock}</td> + * <td> + * <p> + * A {@link eu.mulk.jgvariant.ostree.DeltaSuperblock} to get from the source commit + * referenced by the first checksum encoded in the path to the target commit referenced + * in the second checksum encoded in the path. + * </p> + * + * <p> + * The individual parts of the file path are defined as follows: + * </p> + * + * <dl> + * <dt>{@code {checksumHead}} + * <dd> + * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + * <dt>{@code {checksumRest}} + * <dd> + * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + * </dl> + * </td> + * </tr> + * + * <tr> + * <td>{@code deltas/{sourceChecksumMbase64Head}/{sourceChecksumMbase64Rest}-{targetChecksumMbase64}/{digit...}}</td> + * <td> + * <p> + * A {@link eu.mulk.jgvariant.ostree.DeltaPartPayload} belonging to a delta that goes from + * the source commit referenced by the first checksum encoded in the path to the target + * commit referenced in the second checksum encoded in the path. + * </p> + * + * <p> + * The individual parts of the file path are defined as follows: + * </p> + * + * <dl> + * <dt>{@code {checksumHead}} + * <dd> + * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + * <dt>{@code {checksumRest}} + * <dd> + * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + * </dl> + * </td> + * </tr> + * </table> */ @API(status = Status.EXPERIMENTAL) package eu.mulk.jgvariant.ostree; diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java new file mode 100644 index 0000000..a5fa2b4 --- /dev/null +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java @@ -0,0 +1,70 @@ +package eu.mulk.jgvariant.ostree; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ByteStringTest { + + @Test + void testToModifiedBase64() { + assertEquals("MciDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI", testByteString1.modifiedBase64()); + } + + @Test + void testOfModifiedBase64() { + assertEquals( + testByteString1, + ByteString.ofModifiedBase64("MciDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI")); + } + + @Test + void testToHex() { + assertEquals( + "31c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52", testByteString1.hex()); + } + + @Test + void testOfHex() { + assertEquals( + testByteString1, + ByteString.ofHex("31c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52")); + } + + private static final ByteString testByteString1 = + new ByteString( + new byte[] { + (byte) 0x31, + (byte) 0xc8, + (byte) 0x83, + (byte) 0x5d, + (byte) 0x5c, + (byte) 0x9d, + (byte) 0x2c, + (byte) 0x66, + (byte) 0x87, + (byte) 0xa5, + (byte) 0x00, + (byte) 0x91, + (byte) 0xc8, + (byte) 0x51, + (byte) 0x42, + (byte) 0xd1, + (byte) 0xb2, + (byte) 0xd8, + (byte) 0x53, + (byte) 0xff, + (byte) 0x41, + (byte) 0x6a, + (byte) 0x9f, + (byte) 0xb8, + (byte) 0x1b, + (byte) 0x4e, + (byte) 0xe3, + (byte) 0x07, + (byte) 0x54, + (byte) 0x51, + (byte) 0x0d, + (byte) 0x52 + }); +} diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java index db222f9..031b1cd 100644 --- a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java @@ -2,7 +2,6 @@ package eu.mulk.jgvariant.ostree; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import com.adelean.inject.resources.junit.jupiter.GivenBinaryResource; import com.adelean.inject.resources.junit.jupiter.TestWithResources; @@ -38,11 +37,6 @@ class OstreeDecoderTest { byte[] deltaPartPayloadBytes; @Test - void testTrivial() { - assertTrue(true); - } - - @Test void testSummaryDecoder() { var decoder = Summary.decoder(); var summary = decoder.decode(ByteBuffer.wrap(summaryBytes)); |