Skip to content

Commit

Permalink
[ARSC] Short typed offset array and compact resource entries
Browse files Browse the repository at this point in the history
  • Loading branch information
REAndroid committed Mar 26, 2024
1 parent afbd1bc commit 3b0795d
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 103 deletions.
55 changes: 55 additions & 0 deletions src/main/java/com/reandroid/arsc/array/ShortOffsetArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.array;

import com.reandroid.arsc.item.ShortArrayBlock;

public class ShortOffsetArray extends ShortArrayBlock implements OffsetArray {
public ShortOffsetArray(){
super();
}
@Override
public int getOffset(int i){
int offset = super.get(i);
if(offset == 0xffff){
offset = NO_ENTRY;
}else {
offset = offset * 4;
}
return offset;
}

@Override
public void setOffset(int index, int value){
if(value != NO_ENTRY){
value = value / 4;
}
super.put(index, value);
}
@Override
public int[] getOffsets(){
int length = size();
int[] result = new int[length];
for(int i = 0; i < length; i++){
result[i] = getOffset(i);
}
return result;
}
@Override
public void clear(){
super.setSize(0);
}
}
16 changes: 10 additions & 6 deletions src/main/java/com/reandroid/arsc/array/TypeBlockArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,15 @@ public TypeBlock getOrCreate(ResConfig resConfig){
return getOrCreate(resConfig, false);
}
public TypeBlock getOrCreate(ResConfig resConfig, boolean sparse){
return getOrCreate(resConfig, sparse, false);
}
public TypeBlock getOrCreate(ResConfig resConfig, boolean sparse, boolean offset16){
TypeBlock typeBlock = getTypeBlock(resConfig, sparse);
if(typeBlock != null){
return typeBlock;
}
byte id = getTypeId();
typeBlock = createNext(sparse);
typeBlock = createNext(sparse, offset16);
typeBlock.setTypeId(id);
ResConfig config = typeBlock.getResConfig();
config.copyFrom(resConfig);
Expand Down Expand Up @@ -302,17 +305,17 @@ protected boolean remove(TypeBlock block, boolean trim){
@Override
public TypeBlock newInstance() {
byte id = getTypeId();
TypeBlock typeBlock = new TypeBlock(false);
TypeBlock typeBlock = new TypeBlock(false, false);
typeBlock.setTypeId(id);
return typeBlock;
}
@Override
public TypeBlock[] newArrayInstance(int len) {
return new TypeBlock[len];
}
public TypeBlock createNext(boolean sparse){
public TypeBlock createNext(boolean sparse, boolean offset16){
byte id = getTypeId();
TypeBlock typeBlock = new TypeBlock(sparse);
TypeBlock typeBlock = new TypeBlock(sparse, offset16);
typeBlock.setTypeId(id);
add(typeBlock);
return typeBlock;
Expand All @@ -337,7 +340,7 @@ private boolean readTypeBlockArray(BlockReader reader) throws IOException{
if(chunkType!=ChunkType.TYPE){
return false;
}
TypeHeader typeHeader = reader.readTypeHeader();
TypeHeader typeHeader = TypeHeader.read(reader);
int id = getTypeId();
if(id!=0 && typeHeader.getId().unsignedInt() != id){
return false;
Expand Down Expand Up @@ -406,7 +409,8 @@ public void fromJson(JSONArray json) {
for(int i = 0; i < length; i++){
JSONObject jsonObject = json.getJSONObject(i);
TypeBlock typeBlock = createNext(
jsonObject.optBoolean(TypeBlock.NAME_is_sparse, false));
jsonObject.optBoolean(TypeBlock.NAME_is_sparse, false),
jsonObject.optBoolean(TypeBlock.NAME_is_offset16, false));
typeBlock.fromJson(jsonObject);
}
}
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/com/reandroid/arsc/chunk/TypeBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
*/
package com.reandroid.arsc.chunk;

import com.reandroid.arsc.array.EntryArray;
import com.reandroid.arsc.array.IntegerOffsetArray;
import com.reandroid.arsc.array.SparseOffsetsArray;
import com.reandroid.arsc.array.*;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.header.TypeHeader;
import com.reandroid.arsc.item.*;
Expand All @@ -44,22 +43,28 @@ public class TypeBlock extends Chunk<TypeHeader>

private final EntryArray mEntryArray;
private TypeString mTypeString;
public TypeBlock(boolean sparse) {
super(new TypeHeader(sparse), 2);
public TypeBlock(boolean sparse, boolean offset16) {
super(new TypeHeader(sparse, offset16), 2);
TypeHeader header = getHeaderBlock();

IntegerOffsetArray entryOffsets;
OffsetArray entryOffsets;
if(sparse){
entryOffsets = new SparseOffsetsArray();
}else if(offset16){
entryOffsets = new ShortOffsetArray();
}else {
entryOffsets = new IntegerOffsetArray();
}
this.mEntryArray = new EntryArray(entryOffsets,
header.getCountItem(), header.getEntriesStart());

addChild(entryOffsets);
addChild((Block) entryOffsets);
addChild(mEntryArray);
}
@Deprecated
public TypeBlock(boolean sparse) {
this(sparse, false);
}

public Iterator<ValueItem> allValues(){
return new MergingIterator<>( new ComputeIterator<>(getEntries(),
Expand Down Expand Up @@ -98,6 +103,9 @@ public void linkSpecStringsInternal(SpecStringPool specStringPool){
public boolean isSparse(){
return getHeaderBlock().isSparse();
}
public boolean isOffset16(){
return getHeaderBlock().isOffset16();
}
public void destroy(){
getEntryArray().destroy();
setId(0);
Expand Down Expand Up @@ -319,6 +327,9 @@ public JSONObject toJson() {
if(isSparse()){
jsonObject.put(NAME_is_sparse, true);
}
if(isOffset16()){
jsonObject.put(NAME_is_offset16, true);
}
jsonObject.put(NAME_id, getId());
jsonObject.put(NAME_name, getTypeName());
jsonObject.put(NAME_config, getResConfig().toJson());
Expand Down Expand Up @@ -419,4 +430,5 @@ private static boolean isWildTypeNamePrefix(char ch){
public static final String NAME_id = "id";
public static final String NAME_entries = "entries";
public static final String NAME_is_sparse = "is_sparse";
public static final String NAME_is_offset16 = "is_offset16";
}
7 changes: 4 additions & 3 deletions src/main/java/com/reandroid/arsc/container/PackageBody.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,15 @@ private boolean readNextBlock(BlockReader reader) throws IOException {
return pos!=reader.getPosition();
}
private void readSpecBlock(BlockReader reader) throws IOException{
SpecHeader specHeader = reader.readSpecHeader();
SpecHeader specHeader = SpecHeader.read(reader);
SpecTypePair specTypePair = mSpecTypePairArray.getOrCreate(specHeader.getId().get());
specTypePair.getSpecBlock().readBytes(reader);
}
private void readTypeBlock(BlockReader reader) throws IOException{
TypeHeader typeHeader = reader.readTypeHeader();
TypeHeader typeHeader = TypeHeader.read(reader);
SpecTypePair specTypePair = mSpecTypePairArray.getOrCreate(typeHeader.getId().get());
TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext(typeHeader.isSparse());
TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext(
typeHeader.isSparse(), typeHeader.isOffset16());
typeBlock.readBytes(reader);
}
private void readLibraryBlock(BlockReader reader) throws IOException{
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/reandroid/arsc/container/SpecTypePair.java
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,9 @@ public void onReadBytes(BlockReader reader) throws IOException {
mSpecBlock.readBytes(reader);
}
private void readTypeBlock(BlockReader reader) throws IOException {
TypeHeader typeHeader = reader.readTypeHeader();
TypeBlock typeBlock = mTypeBlockArray.createNext(typeHeader.isSparse());
TypeHeader typeHeader = TypeHeader.read(reader);
TypeBlock typeBlock = mTypeBlockArray.createNext(
typeHeader.isSparse(), typeHeader.isOffset16());
typeBlock.readBytes(reader);
}
private void readUnexpectedNonSpecBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/reandroid/arsc/header/InfoHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,16 @@ public static InfoHeader readHeaderBlock(byte[] bytes) throws IOException {
return infoHeader;
}

public static InfoHeader read(BlockReader reader) throws IOException {
InfoHeader infoHeader = new InfoHeader();
if(reader.available() < infoHeader.getMinimumSize()){
return null;
}
int pos = reader.getPosition();
infoHeader.readBytes(reader);
reader.seek(pos);
return infoHeader;
}

public static final int INFO_MIN_SIZE = 8;
}
16 changes: 15 additions & 1 deletion src/main/java/com/reandroid/arsc/header/SpecHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
package com.reandroid.arsc.header;

import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.ByteItem;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ShortItem;

public class SpecHeader extends HeaderBlock{
import java.io.IOException;

public class SpecHeader extends HeaderBlock{
private final ByteItem id;
private final IntegerItem entryCount;
public SpecHeader() {
Expand Down Expand Up @@ -49,4 +52,15 @@ public String toString(){
+" {id="+getId().toHex()
+", entryCount=" + getEntryCount() + '}';
}

public static SpecHeader read(BlockReader reader) throws IOException {
SpecHeader specHeader = new SpecHeader();
if(reader.available() < specHeader.countBytes()){
throw new IOException("Too few bytes to read spec header, available = " + reader.available());
}
int pos = reader.getPosition();
specHeader.readBytes(reader);
reader.seek(pos);
return specHeader;
}
}
35 changes: 33 additions & 2 deletions src/main/java/com/reandroid/arsc/header/TypeHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@
package com.reandroid.arsc.header;

import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.ByteItem;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ShortItem;
import com.reandroid.arsc.value.ResConfig;

public class TypeHeader extends HeaderBlock{
import java.io.IOException;

public class TypeHeader extends HeaderBlock{
private final ByteItem id;
private final ByteItem flags;
private final IntegerItem count;
private final IntegerItem entriesStart;
private final ResConfig config;
public TypeHeader(boolean sparse) {
public TypeHeader(boolean sparse, boolean offset16) {
super(ChunkType.TYPE.ID);
this.id = new ByteItem();
this.flags = new ByteItem();
Expand All @@ -43,6 +46,11 @@ public TypeHeader(boolean sparse) {
addChild(entriesStart);
addChild(config);
setSparse(sparse);
setOffset16(offset16);
}
@Deprecated
public TypeHeader(boolean sparse) {
this(sparse, false);
}
public boolean isSparse(){
return (getFlags().get() & FLAG_SPARSE) == FLAG_SPARSE;
Expand All @@ -56,6 +64,18 @@ public void setSparse(boolean sparse){
}
getFlags().set(flag);
}
public boolean isOffset16(){
return getFlags().get() == FLAG_OFFSET16;
}
public void setOffset16(boolean offset16){
byte flag = getFlags().get();
if(offset16){
flag = (byte) (flag | FLAG_OFFSET16);
}else {
flag = (byte) (flag & (~FLAG_OFFSET16 & 0xff));
}
getFlags().set(flag);
}

@Override
public int getMinimumSize(){
Expand Down Expand Up @@ -89,8 +109,19 @@ public String toString(){
+", entriesStart=" + getEntriesStart()
+", config=" + getConfig() + '}';
}
public static TypeHeader read(BlockReader reader) throws IOException {
TypeHeader typeHeader = new TypeHeader(false, false);
if(reader.available() < typeHeader.getMinimumSize()){
throw new IOException("Too few bytes to read type header, available = " + reader.available());
}
int pos = reader.getPosition();
typeHeader.readBytes(reader);
reader.seek(pos);
return typeHeader;
}

private static final byte FLAG_SPARSE = 0x1;
private static final byte FLAG_OFFSET16 = 0x2;

//typeHeader.countBytes() - getConfig().countBytes() + ResConfig.SIZE_16
private static final int TYPE_MIN_SIZE = 36;
Expand Down
Loading

0 comments on commit 3b0795d

Please sign in to comment.