Skip to content

Commit fbdab16

Browse files
committed
Fixed #94
1 parent 623c108 commit fbdab16

9 files changed

Lines changed: 177 additions & 13 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package xyz.gianlu.librespot.common;
2+
3+
4+
import java.io.ByteArrayOutputStream;
5+
6+
/**
7+
* A Base62 encoder/decoder.
8+
*
9+
* @author Sebastian Ruhleder, [email protected]
10+
*/
11+
public class Base62 {
12+
private static final int STANDARD_BASE = 256;
13+
private static final int TARGET_BASE = 62;
14+
private final byte[] alphabet;
15+
private byte[] lookup;
16+
17+
private Base62(byte[] alphabet) {
18+
this.alphabet = alphabet;
19+
createLookupTable();
20+
}
21+
22+
/**
23+
* Creates a {@link Base62} instance. Defaults to the GMP-style character set.
24+
*
25+
* @return a {@link Base62} instance.
26+
*/
27+
public static Base62 createInstance() {
28+
return createInstanceWithGmpCharacterSet();
29+
}
30+
31+
/**
32+
* Creates a {@link Base62} instance using the GMP-style character set.
33+
*
34+
* @return a {@link Base62} instance.
35+
*/
36+
public static Base62 createInstanceWithGmpCharacterSet() {
37+
return new Base62(CharacterSets.GMP);
38+
}
39+
40+
/**
41+
* Creates a {@link Base62} instance using the inverted character set.
42+
*
43+
* @return a {@link Base62} instance.
44+
*/
45+
public static Base62 createInstanceWithInvertedCharacterSet() {
46+
return new Base62(CharacterSets.INVERTED);
47+
}
48+
49+
/**
50+
* Encodes a sequence of bytes in Base62 encoding.
51+
*
52+
* @param message a byte sequence.
53+
* @return a sequence of Base62-encoded bytes.
54+
*/
55+
public byte[] encode(byte[] message) {
56+
byte[] indices = convert(message, STANDARD_BASE, TARGET_BASE);
57+
return translate(indices, alphabet);
58+
}
59+
60+
/**
61+
* Decodes a sequence of Base62-encoded bytes.
62+
*
63+
* @param encoded a sequence of Base62-encoded bytes.
64+
* @return a byte sequence.
65+
*/
66+
public byte[] decode(byte[] encoded) {
67+
byte[] prepared = translate(encoded, lookup);
68+
return convert(prepared, TARGET_BASE, STANDARD_BASE);
69+
}
70+
71+
/**
72+
* Uses the elements of a byte array as indices to a dictionary and returns the corresponding values
73+
* in form of a byte array.
74+
*/
75+
private byte[] translate(byte[] indices, byte[] dictionary) {
76+
byte[] translation = new byte[indices.length];
77+
for (int i = 0; i < indices.length; i++)
78+
translation[i] = dictionary[indices[i]];
79+
80+
return translation;
81+
}
82+
83+
/**
84+
* Converts a byte array from a source base to a target base using the alphabet.
85+
*/
86+
private byte[] convert(byte[] message, int sourceBase, int targetBase) {
87+
// This algorithm is inspired by: http://codegolf.stackexchange.com/a/21672
88+
89+
int estimatedLength = estimateOutputLength(message.length, sourceBase, targetBase);
90+
ByteArrayOutputStream out = new ByteArrayOutputStream(estimatedLength);
91+
byte[] source = message;
92+
while (source.length > 0) {
93+
ByteArrayOutputStream quotient = new ByteArrayOutputStream(source.length);
94+
int remainder = 0;
95+
for (byte b : source) {
96+
int accumulator = (b & 0xFF) + remainder * sourceBase;
97+
int digit = (accumulator - (accumulator % targetBase)) / targetBase;
98+
remainder = accumulator % targetBase;
99+
if (quotient.size() > 0 || digit > 0)
100+
quotient.write(digit);
101+
}
102+
103+
out.write(remainder);
104+
source = quotient.toByteArray();
105+
}
106+
107+
// pad output with zeroes corresponding to the number of leading zeroes in the message
108+
for (int i = 0; i < estimatedLength - out.size(); i++)
109+
out.write(0);
110+
111+
return reverse(out.toByteArray());
112+
}
113+
114+
/**
115+
* Estimates the length of the output in bytes.
116+
*/
117+
private int estimateOutputLength(int inputLength, int sourceBase, int targetBase) {
118+
return (int) Math.ceil((Math.log(sourceBase) / Math.log(targetBase)) * inputLength);
119+
}
120+
121+
/**
122+
* Reverses a byte array.
123+
*/
124+
private byte[] reverse(byte[] arr) {
125+
int length = arr.length;
126+
byte[] reversed = new byte[length];
127+
for (int i = 0; i < length; i++)
128+
reversed[length - i - 1] = arr[i];
129+
130+
return reversed;
131+
}
132+
133+
/**
134+
* Creates the lookup table from character to index of character in character set.
135+
*/
136+
private void createLookupTable() {
137+
lookup = new byte[256];
138+
for (int i = 0; i < alphabet.length; i++)
139+
lookup[alphabet[i]] = (byte) (i & 0xFF);
140+
}
141+
142+
private static class CharacterSets {
143+
private static final byte[] GMP = {
144+
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
145+
(byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
146+
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
147+
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
148+
(byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
149+
(byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
150+
(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
151+
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z'
152+
};
153+
private static final byte[] INVERTED = {
154+
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
155+
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
156+
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
157+
(byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
158+
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
159+
(byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
160+
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
161+
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z'
162+
};
163+
}
164+
}
165+

common/src/main/java/xyz/gianlu/librespot/common/Utils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.lang.reflect.Modifier;
2121
import java.math.BigInteger;
2222
import java.nio.ByteBuffer;
23+
import java.nio.charset.StandardCharsets;
2324
import java.security.Permission;
2425
import java.security.PermissionCollection;
2526
import java.util.Arrays;
@@ -47,7 +48,7 @@ public static String decodeGZip(@NotNull ByteString bytes) throws IOException {
4748
int count;
4849
while ((count = gzis.read(buffer)) != -1)
4950
dataOut.write(buffer, 0, count);
50-
return new String(dataOut.toByteArray(), "UTF-8");
51+
return new String(dataOut.toByteArray(), StandardCharsets.UTF_8);
5152
}
5253
}
5354

@@ -165,7 +166,7 @@ public static String[] split(@NotNull String str, char c) {
165166
if (i == -1) {
166167
split[j] = tmp;
167168
} else {
168-
split[j] = tmp.substring(i + 1, tmp.length());
169+
split[j] = tmp.substring(i + 1);
169170
tmp = tmp.substring(0, i);
170171
}
171172
}

core/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@
6060
<artifactId>protobuf-java</artifactId>
6161
<version>${protobuf.version}</version>
6262
</dependency>
63-
<dependency>
64-
<groupId>io.seruco.encoding</groupId>
65-
<artifactId>base62</artifactId>
66-
<version>0.1.2</version>
67-
</dependency>
6863

6964
<!-- Audio -->
7065
<dependency>

core/src/main/java/xyz/gianlu/librespot/mercury/model/AlbumId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.gianlu.librespot.mercury.model;
22

3-
import io.seruco.encoding.base62.Base62;
43
import org.jetbrains.annotations.NotNull;
4+
import xyz.gianlu.librespot.common.Base62;
55
import xyz.gianlu.librespot.common.Utils;
66

77
import java.util.regex.Matcher;

core/src/main/java/xyz/gianlu/librespot/mercury/model/ArtistId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.gianlu.librespot.mercury.model;
22

3-
import io.seruco.encoding.base62.Base62;
43
import org.jetbrains.annotations.NotNull;
4+
import xyz.gianlu.librespot.common.Base62;
55
import xyz.gianlu.librespot.common.Utils;
66

77
import java.util.regex.Matcher;

core/src/main/java/xyz/gianlu/librespot/mercury/model/EpisodeId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.gianlu.librespot.mercury.model;
22

3-
import io.seruco.encoding.base62.Base62;
43
import org.jetbrains.annotations.NotNull;
4+
import xyz.gianlu.librespot.common.Base62;
55
import xyz.gianlu.librespot.common.Utils;
66
import xyz.gianlu.librespot.common.proto.Spirc;
77

core/src/main/java/xyz/gianlu/librespot/mercury/model/ShowId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.gianlu.librespot.mercury.model;
22

3-
import io.seruco.encoding.base62.Base62;
43
import org.jetbrains.annotations.NotNull;
4+
import xyz.gianlu.librespot.common.Base62;
55
import xyz.gianlu.librespot.common.Utils;
66

77
import java.util.regex.Matcher;

core/src/main/java/xyz/gianlu/librespot/mercury/model/TrackId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.gianlu.librespot.mercury.model;
22

3-
import io.seruco.encoding.base62.Base62;
43
import org.jetbrains.annotations.NotNull;
4+
import xyz.gianlu.librespot.common.Base62;
55
import xyz.gianlu.librespot.common.Utils;
66
import xyz.gianlu.librespot.common.proto.Spirc;
77

core/src/main/java/xyz/gianlu/librespot/player/tracks/PlaylistProvider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public void unshuffleTracks() {
8181

8282
int size = tracks.size() - 1;
8383
int[] exchanges = getShuffleExchanges(size, shuffleSeed);
84-
for (int i = 2; i < size; i++) {
84+
for (int i = 1; i < size; i++) {
8585
int n = exchanges[size - i - 1];
8686
Collections.swap(tracks, i, n + 1);
8787
}
@@ -121,6 +121,9 @@ public void unshuffleTracks() {
121121
}
122122
}
123123

124+
if (rebuildState.isEmpty())
125+
throw new IllegalStateException("State cannot be empty!");
126+
124127
state.clearTrack();
125128
state.addAllTrack(rebuildState);
126129
state.setPlayingTrackIndex(0);

0 commit comments

Comments
 (0)