org.robolectric.res.android.Chunk Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
package org.robolectric.res.android;
import static org.robolectric.res.android.Util.dtohl;
import static org.robolectric.res.android.Util.dtohs;
import static org.robolectric.res.android.Util.isTruthy;
import java.nio.ByteBuffer;
import org.robolectric.res.android.ResourceTypes.ResChunk_header;
import org.robolectric.res.android.ResourceTypes.ResStringPool_header;
import org.robolectric.res.android.ResourceTypes.ResTableStagedAliasEntry;
import org.robolectric.res.android.ResourceTypes.ResTableStagedAliasHeader;
import org.robolectric.res.android.ResourceTypes.ResTable_header;
import org.robolectric.res.android.ResourceTypes.ResTable_lib_entry;
import org.robolectric.res.android.ResourceTypes.ResTable_lib_header;
import org.robolectric.res.android.ResourceTypes.ResTable_package;
import org.robolectric.res.android.ResourceTypes.ResTable_type;
import org.robolectric.res.android.ResourceTypes.WithOffset;
// transliterated from
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ChunkIterator.cpp and
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/Chunk.h
// Helpful wrapper around a ResChunk_header that provides getter methods
// that handle endianness conversions and provide access to the data portion
// of the chunk.
class Chunk {
// public:
Chunk(ResChunk_header chunk) {
this.device_chunk_ = chunk;
}
// Returns the type of the chunk. Caller need not worry about endianness.
int type() {
return dtohs(device_chunk_.type);
}
// Returns the size of the entire chunk. This can be useful for skipping
// over the entire chunk. Caller need not worry about endianness.
int size() {
return dtohl(device_chunk_.size);
}
// Returns the size of the header. Caller need not worry about endianness.
int header_size() {
return dtohs(device_chunk_.headerSize);
}
// template
// T* header() {
// if (header_size() >= MinSize) {
// return reinterpret_cast(device_chunk_);
// }
// return nullptr;
// }
ByteBuffer myBuf() {
return device_chunk_.myBuf();
}
int myOffset() {
return device_chunk_.myOffset();
}
public WithOffset data_ptr() {
return new WithOffset(device_chunk_.myBuf(), device_chunk_.myOffset() + header_size());
}
int data_size() {
return size() - header_size();
}
// private:
private ResChunk_header device_chunk_;
public ResTable_header asResTable_header() {
if (header_size() >= ResTable_header.SIZEOF) {
return new ResTable_header(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResStringPool_header asResStringPool_header() {
if (header_size() >= ResStringPool_header.SIZEOF) {
return new ResStringPool_header(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTable_package asResTable_package(int size) {
if (header_size() >= size) {
return new ResTable_package(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTable_type asResTable_type(int size) {
if (header_size() >= size) {
return new ResTable_type(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTable_lib_header asResTable_lib_header() {
if (header_size() >= ResTable_lib_header.SIZEOF) {
return new ResTable_lib_header(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTable_lib_entry asResTable_lib_entry() {
if (header_size() >= ResTable_lib_entry.SIZEOF) {
return new ResTable_lib_entry(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTableStagedAliasHeader asResTableStagedAliasHeader() {
if (header_size() >= ResTableStagedAliasHeader.SIZEOF) {
return new ResTableStagedAliasHeader(device_chunk_.myBuf(), device_chunk_.myOffset());
} else {
return null;
}
}
public ResTableStagedAliasEntry asResTableStagedAliasEntry() {
if (data_size() >= ResTableStagedAliasEntry.SIZEOF) {
return new ResTableStagedAliasEntry(
device_chunk_.myBuf(), device_chunk_.myOffset() + header_size());
} else {
return null;
}
}
static class Iterator {
private ResChunk_header next_chunk_;
private int len_;
private String last_error_;
private boolean last_error_was_fatal_ = true;
public Iterator(WithOffset buf, int itemSize) {
this.next_chunk_ = new ResChunk_header(buf.myBuf(), buf.myOffset());
this.len_ = itemSize;
}
boolean HasNext() { return !HadError() && len_ != 0; };
// Returns whether there was an error and processing should stop
boolean HadError() { return last_error_ != null; }
String GetLastError() { return last_error_; }
// Returns whether there was an error and processing should stop. For legacy purposes,
// some errors are considered "non fatal". Fatal errors stop processing new chunks and
// throw away any chunks already processed. Non fatal errors also stop processing new
// chunks, but, will retain and use any valid chunks already processed.
boolean HadFatalError() { return HadError() && last_error_was_fatal_; }
Chunk Next() {
assert (len_ != 0) : "called Next() after last chunk";
ResChunk_header this_chunk = next_chunk_;
// We've already checked the values of this_chunk, so safely increment.
// next_chunk_ = reinterpret_cast(
// reinterpret_cast(this_chunk) + dtohl(this_chunk->size));
int remaining = len_ - dtohl(this_chunk.size);
if (remaining <= 0) {
next_chunk_ = null;
} else {
next_chunk_ = new ResChunk_header(
this_chunk.myBuf(), this_chunk.myOffset() + dtohl(this_chunk.size));
}
len_ -= dtohl(this_chunk.size);
if (len_ != 0) {
// Prepare the next chunk.
if (VerifyNextChunkNonFatal()) {
VerifyNextChunk();
}
}
return new Chunk(this_chunk);
}
// Returns false if there was an error. For legacy purposes.
boolean VerifyNextChunkNonFatal() {
if (len_ < ResChunk_header.SIZEOF) {
last_error_ = "not enough space for header";
last_error_was_fatal_ = false;
return false;
}
int size = dtohl(next_chunk_.size);
if (size > len_) {
last_error_ = "chunk size is bigger than given data";
last_error_was_fatal_ = false;
return false;
}
return true;
}
// Returns false if there was an error.
boolean VerifyNextChunk() {
// uintptr_t header_start = reinterpret_cast(next_chunk_);
int header_start = next_chunk_.myOffset();
// This data must be 4-byte aligned, since we directly
// access 32-bit words, which must be aligned on
// certain architectures.
if (isTruthy(header_start & 0x03)) {
last_error_ = "header not aligned on 4-byte boundary";
return false;
}
if (len_ < ResChunk_header.SIZEOF) {
last_error_ = "not enough space for header";
return false;
}
int header_size = dtohs(next_chunk_.headerSize);
int size = dtohl(next_chunk_.size);
if (header_size < ResChunk_header.SIZEOF) {
last_error_ = "header size too small";
return false;
}
if (header_size > size) {
last_error_ = "header size is larger than entire chunk";
return false;
}
if (size > len_) {
last_error_ = "chunk size is bigger than given data";
return false;
}
if (isTruthy((size | header_size) & 0x03)) {
last_error_ = "header sizes are not aligned on 4-byte boundary";
return false;
}
return true;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy