mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-30 22:34:24 +02:00
improve XML encoding speed
This commit is contained in:
parent
3eda5edaf6
commit
50753ca54c
@ -19,6 +19,7 @@ import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
import com.reandroid.lib.arsc.util.ResNameMap;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
import com.reandroid.xml.*;
|
||||
@ -244,9 +245,23 @@ import java.util.*;
|
||||
public final byte id;
|
||||
public String name;
|
||||
public final Map<Byte, Type> typeMap;
|
||||
private final ResNameMap<Integer> mEntryNameMap;
|
||||
public Package(byte id){
|
||||
this.id = id;
|
||||
this.typeMap = new HashMap<>();
|
||||
this.mEntryNameMap = new ResNameMap<>();
|
||||
}
|
||||
public void loadEntryMap(){
|
||||
mEntryNameMap.clear();
|
||||
for(Type type:typeMap.values()){
|
||||
String typeName=type.getName();
|
||||
for(Type.Entry entry: type.entryMap.values()){
|
||||
mEntryNameMap.add(typeName, entry.getName(), entry.getResourceId());
|
||||
}
|
||||
}
|
||||
}
|
||||
public Integer getResourceId(String typeName, String name){
|
||||
return mEntryNameMap.get(typeName, name);
|
||||
}
|
||||
public Type.Entry getEntry(String typeName, String name){
|
||||
Type type=getType(typeName);
|
||||
|
@ -25,6 +25,7 @@
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.item.SpecString;
|
||||
import com.reandroid.lib.arsc.util.FrameworkTable;
|
||||
import com.reandroid.lib.arsc.util.ResNameMap;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
import com.reandroid.lib.common.Frameworks;
|
||||
import com.reandroid.lib.common.ResourceResolver;
|
||||
@ -41,6 +42,7 @@
|
||||
private APKLogger apkLogger;
|
||||
private boolean mForceCreateNamespaces = true;
|
||||
private Set<String> mFrameworkPackageNames;
|
||||
private final ResNameMap<EntryBlock> mLocalResNameMap = new ResNameMap<>();
|
||||
public EncodeMaterials(){
|
||||
}
|
||||
public SpecString getSpecString(String name){
|
||||
@ -48,29 +50,6 @@
|
||||
.get(name)
|
||||
.get(0);
|
||||
}
|
||||
public void addTableStringPool(Collection<String> stringList){
|
||||
getCurrentPackage()
|
||||
.getTableBlock()
|
||||
.getTableStringPool()
|
||||
.addStrings(stringList);
|
||||
}
|
||||
public int resolveAttributeNameReference(String refString){
|
||||
String packageName = null;
|
||||
String type = "attr";
|
||||
String name = refString;
|
||||
int i=refString.lastIndexOf(':');
|
||||
if(i>=0){
|
||||
packageName=refString.substring(0, i);
|
||||
name=refString.substring(i+1);
|
||||
}
|
||||
if(EncodeUtil.isEmpty(packageName)
|
||||
|| packageName.equals(getCurrentPackageName())
|
||||
|| !isFrameworkPackageName(packageName)){
|
||||
|
||||
return resolveLocalResourceId(type, name);
|
||||
}
|
||||
return resolveFrameworkResourceId(packageName, type, name);
|
||||
}
|
||||
public EntryBlock getAttributeBlock(String refString){
|
||||
String packageName = null;
|
||||
String type = "attr";
|
||||
@ -111,34 +90,11 @@
|
||||
}
|
||||
return resolveFrameworkResourceId(packageName, type, name);
|
||||
}
|
||||
|
||||
public EntryBlock resolveEntryReference(String refString){
|
||||
if("@null".equals(refString)){
|
||||
return null;
|
||||
}
|
||||
Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString);
|
||||
if(!matcher.find()){
|
||||
return null;
|
||||
}
|
||||
String prefix=matcher.group(1);
|
||||
String packageName = matcher.group(2);
|
||||
if(packageName!=null && packageName.endsWith(":")){
|
||||
packageName=packageName.substring(0, packageName.length()-1);
|
||||
}
|
||||
String type = matcher.group(4);
|
||||
String name = matcher.group(5);
|
||||
if(EncodeUtil.isEmpty(packageName)
|
||||
|| packageName.equals(getCurrentPackageName())
|
||||
|| !isFrameworkPackageName(packageName)){
|
||||
return getLocalEntryBlock(type, name);
|
||||
}
|
||||
return getFrameworkEntry(packageName, type, name);
|
||||
}
|
||||
public int resolveLocalResourceId(String type, String name){
|
||||
for(ResourceIds.Table.Package pkg:packageIdSet){
|
||||
ResourceIds.Table.Package.Type.Entry entry = pkg.getEntry(type, name);
|
||||
if(entry!=null){
|
||||
return entry.getResourceId();
|
||||
Integer resId = pkg.getResourceId(type, name);
|
||||
if(resId!=null){
|
||||
return resId;
|
||||
}
|
||||
}
|
||||
EntryGroup entryGroup=getLocalEntryGroup(type, name);
|
||||
@ -149,15 +105,6 @@
|
||||
"type="+type+
|
||||
", name="+name);
|
||||
}
|
||||
public int resolveFrameworkResourceId(String type, String name){
|
||||
EntryBlock entryBlock = getFrameworkEntry(type, name);
|
||||
if(entryBlock!=null){
|
||||
return entryBlock.getResourceId();
|
||||
}
|
||||
throw new EncodeException("Framework entry not found: " +
|
||||
"type="+type+
|
||||
", name="+name);
|
||||
}
|
||||
public int resolveFrameworkResourceId(String packageName, String type, String name){
|
||||
EntryBlock entryBlock = getFrameworkEntry(packageName, type, name);
|
||||
if(entryBlock!=null){
|
||||
@ -196,6 +143,22 @@
|
||||
return null;
|
||||
}
|
||||
public EntryBlock getLocalEntryBlock(String type, String name){
|
||||
EntryBlock entryBlock=mLocalResNameMap.get(type, name);
|
||||
if(entryBlock!=null){
|
||||
return entryBlock;
|
||||
}
|
||||
loadLocalEntryBlockMap(type);
|
||||
entryBlock=mLocalResNameMap.get(type, name);
|
||||
if(entryBlock!=null){
|
||||
return entryBlock;
|
||||
}
|
||||
entryBlock= searchLocalEntryBlock(type, name);
|
||||
if(entryBlock!=null){
|
||||
mLocalResNameMap.add(type, name, entryBlock);
|
||||
}
|
||||
return entryBlock;
|
||||
}
|
||||
private EntryBlock searchLocalEntryBlock(String type, String name){
|
||||
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
|
||||
if(type.equals(entryGroup.getTypeName()) &&
|
||||
name.equals(entryGroup.getSpecName())){
|
||||
@ -205,7 +168,7 @@
|
||||
SpecTypePair specTypePair=currentPackage.searchByTypeName(type);
|
||||
if(specTypePair!=null){
|
||||
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
|
||||
for(EntryBlock entryBlock:typeBlock.listEntries()){
|
||||
for(EntryBlock entryBlock:typeBlock.listEntries(true)){
|
||||
if(name.equals(entryBlock.getName())){
|
||||
return entryBlock;
|
||||
}
|
||||
@ -220,7 +183,7 @@
|
||||
specTypePair=packageBlock.searchByTypeName(type);
|
||||
if(specTypePair!=null){
|
||||
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
|
||||
for(EntryBlock entryBlock:typeBlock.listEntries()){
|
||||
for(EntryBlock entryBlock:typeBlock.listEntries(true)){
|
||||
if(name.equals(entryBlock.getName())){
|
||||
return entryBlock;
|
||||
}
|
||||
@ -231,6 +194,20 @@
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private void loadLocalEntryBlockMap(String type){
|
||||
ResNameMap<EntryBlock> localMap = mLocalResNameMap;
|
||||
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
|
||||
SpecTypePair specTypePair=packageBlock.searchByTypeName(type);
|
||||
if(specTypePair!=null){
|
||||
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
|
||||
for(EntryBlock entryBlock:typeBlock.listEntries(true)){
|
||||
localMap.add(entryBlock.getTypeName(),
|
||||
entryBlock.getName(), entryBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public EntryBlock getFrameworkEntry(String type, String name){
|
||||
for(FrameworkTable table:frameworkTables){
|
||||
EntryBlock entryBlock = table.searchEntryBlock(type, name);
|
||||
@ -282,16 +259,12 @@
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public EncodeMaterials setForceCreateNamespaces(boolean force) {
|
||||
this.mForceCreateNamespaces = force;
|
||||
return this;
|
||||
}
|
||||
public EncodeMaterials addPackageIds(Collection<ResourceIds.Table.Package> packageIdList) {
|
||||
this.packageIdSet.addAll(packageIdList);
|
||||
return this;
|
||||
}
|
||||
public EncodeMaterials addPackageIds(ResourceIds.Table.Package packageIds) {
|
||||
packageIds.loadEntryMap();
|
||||
this.packageIdSet.add(packageIds);
|
||||
return this;
|
||||
}
|
||||
|
@ -99,16 +99,23 @@ public class XMLFileEncoder {
|
||||
int count=element.getAttributeCount();
|
||||
for(int i=0;i<count;i++){
|
||||
XMLAttribute attribute=element.getAttributeAt(i);
|
||||
if(attribute instanceof SchemaAttr){
|
||||
continue;
|
||||
}
|
||||
if(SchemaAttr.looksSchema(attribute.getName(), attribute.getValue())){
|
||||
continue;
|
||||
}
|
||||
EntryBlock entryBlock=materials.getAttributeBlock(attribute.getName());
|
||||
int resourceId=0;
|
||||
String name=attribute.getNameWoPrefix();
|
||||
int resourceId=decodeUnknownAttributeHex(name);
|
||||
EntryBlock entryBlock=null;
|
||||
if(resourceId==0){
|
||||
entryBlock=getAttributeBlock(attribute);
|
||||
if(entryBlock!=null){
|
||||
resourceId=entryBlock.getResourceId();
|
||||
}
|
||||
}
|
||||
ResXmlAttribute xmlAttribute =
|
||||
resXmlElement.createAttribute(attribute.getNameWoPrefix(), resourceId);
|
||||
resXmlElement.createAttribute(name, resourceId);
|
||||
String prefix=attribute.getNamePrefix();
|
||||
if(prefix!=null){
|
||||
ResXmlStartNamespace ns = resXmlElement.getStartNamespaceByPrefix(prefix);
|
||||
@ -198,11 +205,37 @@ public class XMLFileEncoder {
|
||||
}
|
||||
}
|
||||
private void addResourceId(ResIdBuilder idBuilder, XMLAttribute attribute){
|
||||
EntryBlock entryBlock = materials.getAttributeBlock(attribute.getName());
|
||||
String name=attribute.getNameWoPrefix();
|
||||
int id=decodeUnknownAttributeHex(name);
|
||||
if(id!=0){
|
||||
idBuilder.add(id, name);
|
||||
return;
|
||||
}
|
||||
EntryBlock entryBlock = getAttributeBlock(attribute);
|
||||
if(entryBlock!=null){
|
||||
idBuilder.add(entryBlock.getResourceId(), entryBlock.getName());
|
||||
}
|
||||
}
|
||||
private int decodeUnknownAttributeHex(String name){
|
||||
if(name.length()==0||name.charAt(0)!='@'){
|
||||
return 0;
|
||||
}
|
||||
name=name.substring(1);
|
||||
if(!ValueDecoder.isHex(name)){
|
||||
return 0;
|
||||
}
|
||||
return ValueDecoder.parseHex(name);
|
||||
}
|
||||
private EntryBlock getAttributeBlock(XMLAttribute attribute){
|
||||
if(attribute instanceof SchemaAttr){
|
||||
return null;
|
||||
}
|
||||
String name=attribute.getName();
|
||||
if(name.indexOf(':')<0){
|
||||
return null;
|
||||
}
|
||||
return materials.getAttributeBlock(name);
|
||||
}
|
||||
private ResXmlStartNamespace forceCreateNamespace(ResXmlElement resXmlElement,
|
||||
int resourceId, String prefix){
|
||||
if(!materials.isForceCreateNamespaces()){
|
||||
|
@ -39,8 +39,7 @@ public class FrameworkTable extends TableBlock {
|
||||
private String mFrameworkTitle;
|
||||
private String mFrameworkName;
|
||||
private String mFrameworkVersion;
|
||||
private Map<String, Map<String, EntryGroup>> mNameGroupMap;
|
||||
private final Object mMapLock=new Object();
|
||||
private ResNameMap<EntryGroup> mNameGroupMap;
|
||||
public FrameworkTable(){
|
||||
super();
|
||||
}
|
||||
@ -57,56 +56,41 @@ public class FrameworkTable extends TableBlock {
|
||||
* Call this if you plan to search entries frequently
|
||||
*/
|
||||
public void loadResourceNameMap(){
|
||||
synchronized (mMapLock){
|
||||
if(mNameGroupMap !=null){
|
||||
return;
|
||||
}
|
||||
Map<String, Map<String, EntryGroup>> typeMap=new HashMap<>();
|
||||
ResNameMap<EntryGroup> resNameMap = mNameGroupMap;
|
||||
if(resNameMap == null){
|
||||
resNameMap = new ResNameMap<>();
|
||||
for(PackageBlock packageBlock:listPackages()){
|
||||
for(EntryGroup group:packageBlock.listEntryGroup()){
|
||||
String type=group.getTypeName();
|
||||
Map<String, EntryGroup> groupMap=typeMap.get(type);
|
||||
if(groupMap==null){
|
||||
groupMap=new HashMap<>();
|
||||
typeMap.put(type, groupMap);
|
||||
}
|
||||
groupMap.put(group.getSpecName(), group);
|
||||
resNameMap.add(group.getTypeName(),
|
||||
group.getSpecName(),
|
||||
group);
|
||||
}
|
||||
}
|
||||
mNameGroupMap = typeMap;
|
||||
mNameGroupMap = resNameMap;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Clears resource name map from memory
|
||||
*/
|
||||
public void clearResourceNameMap(){
|
||||
synchronized (mMapLock){
|
||||
if(mNameGroupMap!=null){
|
||||
mNameGroupMap.clear();
|
||||
mNameGroupMap =null;
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean hasResourceGroupMap(){
|
||||
synchronized (mMapLock){
|
||||
return mNameGroupMap!=null;
|
||||
}
|
||||
}
|
||||
private EntryBlock searchEntryBlockFromMap(String typeName, String entryName){
|
||||
synchronized (mMapLock){
|
||||
if(mNameGroupMap ==null){
|
||||
return null;
|
||||
}
|
||||
Map<String, EntryGroup> groupMap = mNameGroupMap.get(typeName);
|
||||
if(groupMap!=null){
|
||||
EntryGroup group=groupMap.get(entryName);
|
||||
if(group!=null){
|
||||
return group.pickOne();
|
||||
}
|
||||
EntryGroup entryGroup = mNameGroupMap.get(typeName, entryName);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup.pickOne();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public EntryBlock searchEntryBlock(String typeName, String entryName){
|
||||
if(hasResourceGroupMap()){
|
||||
return searchEntryBlockFromMap(typeName, entryName);
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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.lib.arsc.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
65
src/main/java/com/reandroid/lib/arsc/util/ResNameMap.java
Normal file
65
src/main/java/com/reandroid/lib/arsc/util/ResNameMap.java
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.lib.arsc.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResNameMap<VALUE> {
|
||||
private final Object mLock = new Object();
|
||||
private final Map<String, Map<String, VALUE>> mainMap;
|
||||
public ResNameMap(){
|
||||
this.mainMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public VALUE get(String type, String name){
|
||||
synchronized (mLock){
|
||||
if(type==null || name==null){
|
||||
return null;
|
||||
}
|
||||
Map<String, VALUE> valueMap = mainMap.get(type);
|
||||
if(valueMap!=null){
|
||||
return valueMap.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public void add(String type, String name, VALUE value){
|
||||
synchronized (mLock){
|
||||
if(type==null || name==null || value==null){
|
||||
return;
|
||||
}
|
||||
Map<String, VALUE> valueMap = mainMap.get(type);
|
||||
if(valueMap==null){
|
||||
valueMap=new HashMap<>();
|
||||
mainMap.put(type, valueMap);
|
||||
}
|
||||
valueMap.put(name, value);
|
||||
}
|
||||
}
|
||||
public void clear(){
|
||||
synchronized (mLock){
|
||||
for(Map<String, VALUE> valueMap:mainMap.values()){
|
||||
valueMap.clear();
|
||||
}
|
||||
mainMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TYPE_ATTR = "attr";
|
||||
private static final String TYPE_ID = "id";
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user