Add DefaultContentMetadata serialization and deserialization
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188889091
This commit is contained in:
parent
18b1ec7a2f
commit
e565a46a7d
@ -16,20 +16,50 @@
|
|||||||
package com.google.android.exoplayer2.upstream.cache;
|
package com.google.android.exoplayer2.upstream.cache;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/** Default implementation of {@link ContentMetadata}. Values are stored as byte arrays. */
|
/** Default implementation of {@link ContentMetadata}. Values are stored as byte arrays. */
|
||||||
public final class DefaultContentMetadata implements ContentMetadata {
|
public final class DefaultContentMetadata implements ContentMetadata {
|
||||||
|
|
||||||
|
private static final int MAX_VALUE_LENGTH = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes a {@link DefaultContentMetadata} from the given input stream.
|
||||||
|
*
|
||||||
|
* @param input Input stream to read from.
|
||||||
|
* @return a {@link DefaultContentMetadata} instance.
|
||||||
|
* @throws IOException If an error occurs during reading from input.
|
||||||
|
*/
|
||||||
|
public static DefaultContentMetadata readFromStream(DataInputStream input) throws IOException {
|
||||||
|
int size = input.readInt();
|
||||||
|
HashMap<String, byte[]> metadata = new HashMap<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
String name = input.readUTF();
|
||||||
|
int valueSize = input.readInt();
|
||||||
|
if (valueSize < 0 || valueSize > MAX_VALUE_LENGTH) {
|
||||||
|
throw new IOException("Invalid value size: " + valueSize);
|
||||||
|
}
|
||||||
|
byte[] value = new byte[valueSize];
|
||||||
|
input.readFully(value);
|
||||||
|
metadata.put(name, value);
|
||||||
|
}
|
||||||
|
return new DefaultContentMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
private final Map<String, byte[]> metadata;
|
private final Map<String, byte[]> metadata;
|
||||||
|
|
||||||
/** Constructs an empty {@link DefaultContentMetadata}. */
|
/** Constructs an empty {@link DefaultContentMetadata}. */
|
||||||
public DefaultContentMetadata() {
|
public DefaultContentMetadata() {
|
||||||
this.metadata = new HashMap<>();
|
this(Collections.<String, byte[]>emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,53 +67,66 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
|||||||
* applying {@code mutations}.
|
* applying {@code mutations}.
|
||||||
*/
|
*/
|
||||||
public DefaultContentMetadata(DefaultContentMetadata other, ContentMetadataMutations mutations) {
|
public DefaultContentMetadata(DefaultContentMetadata other, ContentMetadataMutations mutations) {
|
||||||
this.metadata = new HashMap<>(other.metadata);
|
this(applyMutations(other.metadata, mutations));
|
||||||
applyMutations(mutations);
|
}
|
||||||
|
|
||||||
|
private DefaultContentMetadata(Map<String, byte[]> metadata) {
|
||||||
|
this.metadata = Collections.unmodifiableMap(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes itself to a {@link DataOutputStream}.
|
||||||
|
*
|
||||||
|
* @param output Output stream to store the values.
|
||||||
|
* @throws IOException If an error occurs during writing values to output.
|
||||||
|
*/
|
||||||
|
public void writeToStream(DataOutputStream output) throws IOException {
|
||||||
|
output.writeInt(metadata.size());
|
||||||
|
for (Entry<String, byte[]> entry : metadata.entrySet()) {
|
||||||
|
output.writeUTF(entry.getKey());
|
||||||
|
byte[] value = entry.getValue();
|
||||||
|
output.writeInt(value.length);
|
||||||
|
output.write(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final byte[] get(String name, byte[] defaultValue) {
|
public final byte[] get(String name, byte[] defaultValue) {
|
||||||
synchronized (metadata) {
|
if (metadata.containsKey(name)) {
|
||||||
if (metadata.containsKey(name)) {
|
return metadata.get(name);
|
||||||
return metadata.get(name);
|
} else {
|
||||||
} else {
|
return defaultValue;
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String get(String name, String defaultValue) {
|
public final String get(String name, String defaultValue) {
|
||||||
synchronized (metadata) {
|
if (metadata.containsKey(name)) {
|
||||||
if (metadata.containsKey(name)) {
|
byte[] bytes = metadata.get(name);
|
||||||
byte[] bytes = metadata.get(name);
|
return new String(bytes, Charset.forName(C.UTF8_NAME));
|
||||||
return new String(bytes, Charset.forName(C.UTF8_NAME));
|
} else {
|
||||||
} else {
|
return defaultValue;
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final long get(String name, long defaultValue) {
|
public final long get(String name, long defaultValue) {
|
||||||
synchronized (metadata) {
|
if (metadata.containsKey(name)) {
|
||||||
if (metadata.containsKey(name)) {
|
byte[] bytes = metadata.get(name);
|
||||||
byte[] bytes = metadata.get(name);
|
return ByteBuffer.wrap(bytes).getLong();
|
||||||
return ByteBuffer.wrap(bytes).getLong();
|
} else {
|
||||||
} else {
|
return defaultValue;
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean contains(String name) {
|
public final boolean contains(String name) {
|
||||||
synchronized (metadata) {
|
return metadata.containsKey(name);
|
||||||
return metadata.containsKey(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyMutations(ContentMetadataMutations mutations) {
|
private static Map<String, byte[]> applyMutations(
|
||||||
|
Map<String, byte[]> otherMetadata, ContentMetadataMutations mutations) {
|
||||||
|
HashMap<String, byte[]> metadata = new HashMap<>(otherMetadata);
|
||||||
List<String> removedValues = mutations.getRemovedValues();
|
List<String> removedValues = mutations.getRemovedValues();
|
||||||
for (int i = 0; i < removedValues.size(); i++) {
|
for (int i = 0; i < removedValues.size(); i++) {
|
||||||
metadata.remove(removedValues.get(i));
|
metadata.remove(removedValues.get(i));
|
||||||
@ -91,8 +134,16 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
|||||||
Map<String, Object> editedValues = mutations.getEditedValues();
|
Map<String, Object> editedValues = mutations.getEditedValues();
|
||||||
for (String name : editedValues.keySet()) {
|
for (String name : editedValues.keySet()) {
|
||||||
Object value = editedValues.get(name);
|
Object value = editedValues.get(name);
|
||||||
metadata.put(name, getBytes(value));
|
byte[] bytes = getBytes(value);
|
||||||
|
if (bytes.length > MAX_VALUE_LENGTH) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"The size of %s (%d) is greater than maximum allowed: %d",
|
||||||
|
name, bytes.length, MAX_VALUE_LENGTH));
|
||||||
|
}
|
||||||
|
metadata.put(name, bytes);
|
||||||
}
|
}
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getBytes(Object value) {
|
private static byte[] getBytes(Object value) {
|
||||||
|
@ -17,6 +17,10 @@ package com.google.android.exoplayer2.upstream.cache;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -30,7 +34,7 @@ public class DefaultContentMetadataTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
contentMetadata = createAbstractContentMetadata();
|
contentMetadata = createContentMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -40,7 +44,7 @@ public class DefaultContentMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContainsReturnsTrueForInitialValue() throws Exception {
|
public void testContainsReturnsTrueForInitialValue() throws Exception {
|
||||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
contentMetadata = createContentMetadata("metadata name", "value");
|
||||||
assertThat(contentMetadata.contains("metadata name")).isTrue();
|
assertThat(contentMetadata.contains("metadata name")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +55,7 @@ public class DefaultContentMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetReturnsInitialValue() throws Exception {
|
public void testGetReturnsInitialValue() throws Exception {
|
||||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
contentMetadata = createContentMetadata("metadata name", "value");
|
||||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +99,7 @@ public class DefaultContentMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditMetadata() throws Exception {
|
public void testEditMetadata() throws Exception {
|
||||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
contentMetadata = createContentMetadata("metadata name", "value");
|
||||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||||
mutations.set("metadata name", "edited value");
|
mutations.set("metadata name", "edited value");
|
||||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||||
@ -104,7 +108,7 @@ public class DefaultContentMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveMetadata() throws Exception {
|
public void testRemoveMetadata() throws Exception {
|
||||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
contentMetadata = createContentMetadata("metadata name", "value");
|
||||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||||
mutations.remove("metadata name");
|
mutations.remove("metadata name");
|
||||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||||
@ -129,7 +133,27 @@ public class DefaultContentMetadataTest {
|
|||||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultContentMetadata createAbstractContentMetadata(String... pairs) {
|
@Test
|
||||||
|
public void testSerializeDeserialize() throws Exception {
|
||||||
|
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||||
|
mutations.set("metadata1 name", "value");
|
||||||
|
mutations.set("metadata2 name", 12345);
|
||||||
|
byte[] metadata3 = {1, 2, 3};
|
||||||
|
mutations.set("metadata3 name", metadata3);
|
||||||
|
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
contentMetadata.writeToStream(new DataOutputStream(outputStream));
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
|
DefaultContentMetadata contentMetadata2 =
|
||||||
|
DefaultContentMetadata.readFromStream(new DataInputStream(inputStream));
|
||||||
|
|
||||||
|
assertThat(contentMetadata2.get("metadata1 name", "default value")).isEqualTo("value");
|
||||||
|
assertThat(contentMetadata2.get("metadata2 name", 0)).isEqualTo(12345);
|
||||||
|
assertThat(contentMetadata2.get("metadata3 name", new byte[] {})).isEqualTo(metadata3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultContentMetadata createContentMetadata(String... pairs) {
|
||||||
assertThat(pairs.length % 2).isEqualTo(0);
|
assertThat(pairs.length % 2).isEqualTo(0);
|
||||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||||
for (int i = 0; i < pairs.length; i += 2) {
|
for (int i = 0; i < pairs.length; i += 2) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user