/* * Copyright 2017-present Facebook, Inc. * * 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.facebook.buck.android.resources; import java.nio.IntBuffer; import java.util.Map; import java.util.SortedSet; import java.util.stream.IntStream; /** * BringToFrontMapper supports "bring-to-front" reassignment of reference ids. Given a set of ids, * it will construct an assignment that ensures that those ids are the first ones for each type. */ public class BringToFrontMapper implements ReferenceMapper { private final int packageId; private final int[][] mapping; private final int[][] rewriters; public BringToFrontMapper(int packageId, int[][] mapping, int[][] rewriters) { this.packageId = packageId; this.mapping = mapping; this.rewriters = rewriters; } /** * Constructs a ReferenceMapper that will reassign ids and adjust offsets such that for each key * in idsByType, the ids in idsByType will be reassigned to the first n ids of that type. The ids * provided should not include package or type ids. */ public static BringToFrontMapper construct( int packageId, Map<Integer, SortedSet<Integer>> idsByType) { int maxType = 0; for (int k : idsByType.keySet()) { maxType = Math.max(k, maxType); } int[][] mapping = new int[maxType + 1][]; int[][] rewriters = new int[maxType + 1][]; idsByType.forEach( (type, idsSet) -> { int maxRef = 0; int[] ids = idsSet.stream().mapToInt(i -> i).toArray(); for (int id : ids) { maxRef = Math.max(maxRef, id); } int[] newMapping = IntStream.range(0, maxRef + 1).toArray(); // rewrite() does swaps iterating forward through ids. Doing those swaps in reverse will give // us a mapping of the new ids. for (int i = ids.length - 1; i >= 0; i--) { // Since ids is sorted, it's guaranteed that newMapping[i] == i; int id = ids[i]; newMapping[i] = newMapping[id]; newMapping[id] = i; } mapping[type] = newMapping; rewriters[type] = ids; }); return new BringToFrontMapper(packageId, mapping, rewriters); } @Override public int map(int id) { if ((id >> 24) != packageId) { return id; } int type = (id >> 16) & 0xFF; if (type < mapping.length) { int[] typeMapping = mapping[type]; if (typeMapping == null) { return id; } int entry = id & 0xFFFF; if (entry < typeMapping.length) { return (id & 0xFFFF0000) | typeMapping[entry]; } } return id; } @Override public void rewrite(int type, IntBuffer buf) { if (type < rewriters.length) { int[] rewriter = rewriters[type]; if (rewriter != null) { for (int i = 0; i < rewriter.length; i++) { int id = rewriter[i]; // Swap the values at the old and new positions. int temp = buf.get(id); buf.put(id, buf.get(i)); buf.put(i, temp); } } } } }