performance: link pool strings when only needed #36

This commit is contained in:
REAndroid 2023-04-28 19:06:52 +02:00
parent c80e0943d3
commit b6bbac2dcf
22 changed files with 695 additions and 530 deletions

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,6 +16,8 @@
package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.Entry;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray;
@ -28,6 +30,20 @@ public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<J
public EntryArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart){
super(offsets, itemCount, itemStart);
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
Iterator<Entry> itr = iterator(true);
while (itr.hasNext()){
Entry entry = itr.next();
entry.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
Iterator<Entry> itr = iterator(true);
while (itr.hasNext()){
Entry entry = itr.next();
entry.linkSpecStringsInternal(specStringPool);
}
}
public int getHighestEntryId(){
if(isSparse()){
return ((SparseOffsetsArray) getOffsetArray()).getHighestId();

View File

@ -27,6 +27,7 @@ import com.reandroid.arsc.header.PackageHeader;
import com.reandroid.arsc.list.OverlayableList;
import com.reandroid.arsc.list.StagedAliasList;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.LibraryInfo;
@ -68,6 +69,16 @@ public class PackageBlock extends Chunk<PackageHeader>
addChild(mSpecStringPool);
addChild(mBody);
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(SpecTypePair specTypePair : listAllSpecTypePair()){
specTypePair.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
for(SpecTypePair specTypePair : listAllSpecTypePair()){
specTypePair.linkSpecStringsInternal(specStringPool);
}
}
public void destroy(){
getEntriesGroupMap().clear();
getPackageBody().destroy();
@ -221,6 +232,7 @@ public class PackageBlock extends Chunk<PackageHeader>
if(!this.entryGroupMapLocked){
return;
}
entryGroupMapLocked = false;
Map<Integer, EntryGroup> map = this.mEntriesGroup;
map.clear();
createEntryGroupMap(map);

View File

@ -53,6 +53,11 @@ public class TableBlock extends Chunk<TableHeader>
addChild(mPackageArray);
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(PackageBlock packageBlock : listPackages()){
packageBlock.linkTableStringsInternal(tableStringPool);
}
}
public List<Entry> resolveReference(int referenceId){
return resolveReference(referenceId, null);
}

View File

@ -24,6 +24,8 @@ import com.reandroid.arsc.header.TypeHeader;
import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig;
@ -59,6 +61,14 @@ public class TypeBlock extends Chunk<TypeHeader>
addChild(entryOffsets);
addChild(mEntryArray);
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
EntryArray entryArray = getEntryArray();
entryArray.linkTableStringsInternal(tableStringPool);
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
EntryArray entryArray = getEntryArray();
entryArray.linkSpecStringsInternal(specStringPool);
}
public boolean isSparse(){
return getHeaderBlock().isSparse();
}

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -13,26 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.chunk.xml;
package com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.value.AttributeValue;
import com.reandroid.arsc.value.ValueItem;
import com.reandroid.arsc.value.ValueType;
import com.reandroid.common.EntryStore;
import com.reandroid.json.JSONObject;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLException;
import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.value.AttributeValue;
import com.reandroid.arsc.value.ValueItem;
import com.reandroid.arsc.value.ValueType;
import com.reandroid.common.EntryStore;
import com.reandroid.json.JSONObject;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLException;
import java.io.IOException;
import java.util.Objects;
import java.io.IOException;
import java.util.Objects;
public class ResXmlAttribute extends ValueItem implements AttributeValue, Comparable<ResXmlAttribute>{
public class ResXmlAttribute extends ValueItem implements AttributeValue, Comparable<ResXmlAttribute>{
private ReferenceItem mNSReference;
private ReferenceItem mNameReference;
private ReferenceItem mNameIdReference;
@ -232,6 +232,7 @@
@Override
public void onReadBytes(BlockReader reader) throws IOException {
super.onReadBytes(reader);
super.onDataLoaded();
linkAll();
linkStartNameSpace();
}
@ -486,4 +487,4 @@
private static final int OFFSET_STRING = 8;
private static final int OFFSET_SIZE = 12;
}
}

View File

@ -25,6 +25,8 @@ import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.TypeHeader;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.TypeString;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig;
import com.reandroid.json.JSONConvert;
@ -55,6 +57,16 @@ public class SpecTypePair extends BlockContainer<Block>
this(new SpecBlock(), new TypeBlockArray());
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(TypeBlock typeBlock:listTypeBlocks()){
typeBlock.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
for(TypeBlock typeBlock:listTypeBlocks()){
typeBlock.linkSpecStringsInternal(specStringPool);
}
}
public Map<Integer, EntryGroup> createEntryGroups(){
Map<Integer, EntryGroup> map = new HashMap<>();
for(TypeBlock typeBlock:listTypeBlocks()){

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,7 +15,6 @@
*/
package com.reandroid.arsc.item;
public class ResXmlString extends StringItem {
public ResXmlString(boolean utf8) {
super(utf8);
@ -24,4 +23,7 @@ public class ResXmlString extends StringItem {
this(utf8);
set(value);
}
@Override
void ensureStringLinkUnlocked(){
}
}

View File

@ -49,11 +49,19 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
mReferencedList.clear();
}
public boolean hasReference(){
ensureStringLinkUnlocked();
return mReferencedList.size()>0;
}
public Collection<ReferenceItem> getReferencedList(){
ensureStringLinkUnlocked();
return mReferencedList;
}
void ensureStringLinkUnlocked(){
StringPool<?> stringPool = getParentInstance(StringPool.class);
if(stringPool != null){
stringPool.ensureStringLinkUnlockedInternal();
}
}
public void addReference(ReferenceItem ref){
if(ref!=null){
mReferencedList.add(ref);
@ -265,11 +273,11 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
}
@Override
public String toString(){
String str=getHtml();
if(str==null){
String str = getHtml();
if(str == null){
return "NULL";
}
return "USED BY="+getReferencedList().size()+"{"+str+"}";
return "USED BY=" + mReferencedList.size() + "{" + str + "}";
}
private static int[] decodeUtf8StringByteLength(byte[] lengthBytes) {

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,9 +16,9 @@
package com.reandroid.arsc.item;
import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.pool.TypeStringPool;
public class TypeString extends StringItem {
public class TypeString extends StringItem {
public TypeString(boolean utf8) {
super(utf8);
}
@ -36,6 +36,9 @@ package com.reandroid.arsc.item;
return null;
}
@Override
void ensureStringLinkUnlocked(){
}
@Override
public String toString(){
return String.format("0x%02x", getId())+':'+get();
}

View File

@ -25,7 +25,7 @@ import java.util.Objects;
public class ResXmlStringPool extends StringPool<ResXmlString> {
public ResXmlStringPool(boolean is_utf8) {
super(is_utf8);
super(is_utf8, false);
}
@Override
public ResXmlString removeReference(ReferenceItem referenceItem){

View File

@ -34,4 +34,12 @@ public class SpecStringPool extends StringPool<SpecString> {
public PackageBlock getPackageBlock(){
return getParent(PackageBlock.class);
}
@Override
void linkStrings(){
PackageBlock packageBlock = getPackageBlock();
if(packageBlock != null){
packageBlock.linkSpecStringsInternal(this);
}
}
}

View File

@ -33,12 +33,14 @@ import java.util.*;
public abstract class StringPool<T extends StringItem> extends Chunk<StringPoolHeader> implements BlockLoad, JSONConvert<JSONArray>, Comparator<String> {
private final Object mLock = new Object();
private final StringArray<T> mArrayStrings;
private final StyleArray mArrayStyles;
private final Map<String, StringGroup<T>> mUniqueMap;
private boolean stringLinkLocked;
StringPool(boolean is_utf8){
StringPool(boolean is_utf8, boolean stringLinkLocked){
super(new StringPoolHeader(), 4);
OffsetArray offsetStrings = new OffsetArray();
@ -68,7 +70,30 @@ public abstract class StringPool<T extends StringItem> extends Chunk<StringPoolH
header.getFlagUtf8().setBlockLoad(this);
mUniqueMap = new HashMap<>();
this.stringLinkLocked = stringLinkLocked;
}
StringPool(boolean is_utf8){
this(is_utf8, true);
}
public boolean isStringLinkLocked(){
return stringLinkLocked;
}
public void ensureStringLinkUnlockedInternal(){
if(!stringLinkLocked){
return;
}
synchronized (mLock){
if(!stringLinkLocked){
return;
}
stringLinkLocked = false;
linkStrings();
}
}
void linkStrings(){
}
public void removeString(T item){
getStringsArray().remove(item);
}

View File

@ -19,11 +19,11 @@ import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.array.TableStringArray;
import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.UnknownChunk;
import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.TableHeader;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.TableString;
@ -35,6 +35,13 @@ public class TableStringPool extends StringPool<TableString> {
super(is_utf8);
}
@Override
void linkStrings(){
TableBlock tableBlock = getParentInstance(TableBlock.class);
if(tableBlock != null){
tableBlock.linkTableStringsInternal(this);
}
}
@Override
StringArray<TableString> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new TableStringArray(offsets, itemCount, itemStart, is_utf8);

View File

@ -27,7 +27,7 @@ import com.reandroid.arsc.item.TypeString;
public class TypeStringPool extends StringPool<TypeString> {
private final IntegerItem mTypeIdOffset;
public TypeStringPool(boolean is_utf8, IntegerItem typeIdOffset) {
super(is_utf8);
super(is_utf8, false);
this.mTypeIdOffset = typeIdOffset;
}
public int idOf(String typeName){

View File

@ -27,6 +27,8 @@ import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
@ -41,6 +43,15 @@ public class Entry extends Block implements JSONConvert<JSONObject> {
super();
}
public void linkTableStringsInternal(TableStringPool tableStringPool){
TableEntry<?, ?> tableEntry = getTableEntry();
tableEntry.linkTableStringsInternal(tableStringPool);
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
TableEntry<?, ?> tableEntry = getTableEntry();
ValueHeader header = tableEntry.getHeader();
header.linkSpecStringsInternal(specStringPool);
}
public ResValue getResValue(){
TableEntry<?, ?> tableEntry = getTableEntry();
if(tableEntry instanceof ResTableEntry){

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,6 +15,7 @@
*/
package com.reandroid.arsc.value;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONObject;
public class ResTableEntry extends TableEntry<EntryHeader, ResValue> {
@ -22,6 +23,10 @@ public class ResTableEntry extends TableEntry<EntryHeader, ResValue> {
super(new EntryHeader(), new ResValue());
}
@Override
void linkTableStringsInternal(TableStringPool tableStringPool){
getValue().linkTableStrings(tableStringPool);
}
@Override
void onRemoved(){
getHeader().onRemoved();

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,6 +16,7 @@
package com.reandroid.arsc.value;
import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONObject;
public class ResTableMapEntry extends TableEntry<EntryHeaderMap, ResValueMapArray> {
@ -42,6 +43,12 @@ public class ResTableMapEntry extends TableEntry<EntryHeaderMap, ResValueMapArra
getValue().setChildesCount(valuesCount);
}
@Override
void linkTableStringsInternal(TableStringPool tableStringPool){
for(ResValueMap resValueMap : listResValueMap()){
resValueMap.linkTableStrings(tableStringPool);
}
}
@Override
void onHeaderLoaded(ValueHeader valueHeader){
getValue().setChildesCount(getValuesCount());
}

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,9 +15,9 @@
*/
package com.reandroid.arsc.value;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.PackageBlock;
public class ResValue extends ValueItem {
public class ResValue extends ValueItem {
public ResValue() {
super(8, OFFSET_SIZE);
}

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@ package com.reandroid.arsc.value;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
@ -96,6 +97,7 @@ public abstract class TableEntry<HEADER extends ValueHeader, VALUE extends Block
}
abstract void onRemoved();
abstract boolean shouldMerge(TableEntry<?, ?> tableEntry);
abstract void linkTableStringsInternal(TableStringPool tableStringPool);
public abstract void merge(TableEntry<?, ?> tableEntry);
@Override

View File

@ -19,6 +19,7 @@ import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.ParentChunk;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
@ -32,6 +33,21 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
writeSize();
putInteger(getBytesInternal(), OFFSET_SPEC_REFERENCE, -1);
}
void linkSpecStringsInternal(SpecStringPool specStringPool){
int key = getKey();
SpecString specString = specStringPool.get(key);
if(specString == null){
mStringReference = null;
return;
}
if(mStringReference != null){
specString.removeReference(mStringReference);
}
ReferenceItem stringReference = new ReferenceBlock<>(this, OFFSET_SPEC_REFERENCE);
mStringReference = stringReference;
specString.addReference(stringReference);
}
public void onRemoved(){
unLinkStringReference();
}
@ -116,7 +132,11 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
}
private void linkStringReference(){
linkStringReference(getNameString());
StringPool<?> specStringPool = getSpecStringPool();
if(specStringPool == null || specStringPool.isStringLinkLocked()){
return;
}
linkStringReference(specStringPool.get(getKey()));
}
private void linkStringReference(StringItem stringItem){
unLinkStringReference();
@ -167,7 +187,6 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
int size = reader.readUnsignedShort();
setBytesLength(size, false);
reader.readFully(getBytesInternal());
linkStringReference();
}
private void setName(String name){
if(name==null){

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,6 +23,7 @@ import com.reandroid.arsc.item.ReferenceBlock;
import com.reandroid.arsc.item.ReferenceItem;
import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
@ -39,6 +40,10 @@ import java.util.Objects;
writeSize();
}
void linkTableStrings(TableStringPool tableStringPool){
linkStringReference(tableStringPool);
}
public void onRemoved(){
unLinkStringReference();
}
@ -78,7 +83,7 @@ import java.util.Objects;
int size = countBytes() - offset;
putShort(getBytesInternal(), offset + OFFSET_SIZE, (short) size);
}
private void onDataLoaded(){
protected void onDataLoaded(){
if(getValueType() == ValueType.STRING){
linkStringReference();
}else {
@ -136,8 +141,16 @@ import java.util.Objects;
}
}
private void linkStringReference(){
StringItem tableString = getDataAsPoolString();
if(tableString==null){
StringPool<?> stringPool = getStringPool();
if(stringPool == null || stringPool.isStringLinkLocked()){
return;
}
linkStringReference(stringPool);
}
private void linkStringReference(StringPool<?> stringPool){
StringItem tableString = stringPool.get(getData());
if(tableString == null){
unLinkStringReference();
return;
}
ReferenceItem stringReference = mStringReference;
@ -181,7 +194,6 @@ import java.util.Objects;
setBytesLength(this.sizeOffset + 8, false);
writeSize();
}
onDataLoaded();
}
private int initializeBytes(BlockReader reader) throws IOException {
int position = reader.getPosition();