blob: cfe363552092739de4c521a91dbf6ec617415d3b [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;
8import java.util.Arrays;
Matthias Andreas Benkardc7aa2b62022-01-23 18:10:03 +01009import java.util.Base64;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010010import java.util.HexFormat;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010011import java.util.stream.IntStream;
12import java.util.stream.Stream;
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010013import org.jetbrains.annotations.Nullable;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010014
15/**
16 * A wrapper for a {@code byte[]} that implements {@link #equals(Object)}, {@link #hashCode()}, and
17 * {@link #toString()} according to value semantics.
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010018 *
19 * @param bytes the byte array that this ByteString wraps.
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010020 */
21public record ByteString(byte[] bytes) {
22
23 private static final Decoder<ByteString> DECODER = Decoder.ofByteArray().map(ByteString::new);
24
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010025 /**
26 * Returns a decoder for a {@code byte[]} that wraps the result in {@link ByteString}.
27 *
28 * @return a possibly shared {@link Decoder}.
29 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010030 public static Decoder<ByteString> decoder() {
31 return DECODER;
32 }
33
34 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010035 public boolean equals(@Nullable Object o) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010036 return (o instanceof ByteString byteString) && Arrays.equals(bytes, byteString.bytes);
37 }
38
39 @Override
40 public int hashCode() {
41 return Arrays.hashCode(bytes);
42 }
43
44 @Override
45 public String toString() {
46 return "ByteString{hex=\"%s\"}".formatted(hex());
47 }
48
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010049 /**
50 * Converts the contained byte array into a hex string.
51 *
52 * <p>Useful for printing.
53 *
54 * @return a hex string representation of this byte string.
55 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010056 public String hex() {
57 return HexFormat.of().formatHex(bytes);
58 }
59
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010060 /**
61 * Parses a hex string into a {@link ByteString}.
62 *
63 * @param hex a hex string.
64 * @return a {@link ByteString} corresponding to the given hex string.
65 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010066 public static ByteString ofHex(String hex) {
67 return new ByteString(HexFormat.of().parseHex(hex));
68 }
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010069
70 /**
Matthias Andreas Benkardc7aa2b62022-01-23 18:10:03 +010071 * Converts the contained byte array into modified Base64 (with {@code "/"} replaced with {@code
72 * "-"}).
73 *
74 * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address
75 * static deltas in an OSTree repository.
76 *
77 * <p>Useful for printing.
78 *
79 * @return a modified Base64 representation of the bytes making up this checksum.
80 */
81 public String modifiedBase64() {
82 return Base64.getEncoder().withoutPadding().encodeToString(bytes).replace('/', '_');
83 }
84
85 /**
86 * Parses a modified Base64 string into a {@link Checksum}.
87 *
88 * <p>Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address
89 * static deltas in an OSTree repository.
90 *
91 * @param mbase64 a hex string.
92 * @return a {@link Checksum} corresponding to the given modified Base64 string.
93 */
94 public static ByteString ofModifiedBase64(String mbase64) {
95 return new ByteString(Base64.getDecoder().decode(mbase64.replace('_', '/')));
96 }
97
98 /**
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010099 * Returns the number of bytes in the byte string.
100 *
101 * @return the number of bytes in the byte string.
102 */
103 public int size() {
104 return bytes.length;
105 }
106
107 /**
108 * Returns a {@link Stream} of all the bytes in the byte string.
109 *
110 * @return a new {@link Stream}.
111 */
112 Stream<Byte> stream() {
113 return IntStream.range(0, bytes.length).mapToObj(i -> bytes[i]);
114 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100115}