View Javadoc
1   package io.jawk.intermediate;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.io.Serializable;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Set;
30  
31  /**
32   * Manages creation and resolution of {@link Address} instances for
33   * {@link AwkTuples}.
34   */
35  class AddressManager implements Serializable {
36  
37  	private static final long serialVersionUID = 1L;
38  
39  	private final Set<Address> unresolvedAddresses = new HashSet<Address>();
40  	private final Map<Integer, Address> addressIndexes = new HashMap<Integer, Address>();
41  	private final Map<String, Integer> addressLabelCounts = new HashMap<String, Integer>();
42  
43  	/**
44  	 * Creates a new unresolved address for the supplied label prefix.
45  	 * <p>
46  	 * Repeated requests for the same label receive monotonically numbered suffixes
47  	 * so tuple generation can distinguish placeholder jump targets until they are
48  	 * resolved to concrete queue indexes.
49  	 * </p>
50  	 *
51  	 * @param label The logical label name used to derive a unique address identity
52  	 * @return A new unresolved {@link Address} tracked by this manager
53  	 */
54  	Address createAddress(String label) {
55  		Integer count = addressLabelCounts.get(label);
56  		if (count == null) {
57  			count = 0;
58  		} else {
59  			count = count + 1;
60  		}
61  		addressLabelCounts.put(label, count);
62  		Address address = new Address(label + "_" + count);
63  		unresolvedAddresses.add(address);
64  		return address;
65  	}
66  
67  	/**
68  	 * Resolves a previously created address to its first concrete tuple index.
69  	 *
70  	 * @param address The unresolved address to bind
71  	 * @param index The tuple index that now owns the address
72  	 * @throws Error If the address is already resolved or belongs to another manager
73  	 */
74  	void resolveAddress(Address address, int index) {
75  		if (unresolvedAddresses.contains(address)) {
76  			unresolvedAddresses.remove(address);
77  			address.assignIndex(index);
78  			addressIndexes.put(index, address);
79  		} else {
80  			throw new Error(address.toString() + " is already resolved, or unresolved from another scope.");
81  		}
82  	}
83  
84  	/**
85  	 * Returns the resolved address currently assigned to a tuple index.
86  	 *
87  	 * @param index The tuple index to inspect
88  	 * @return The resolved {@link Address} for that index, or {@code null} when no
89  	 *         address is bound there
90  	 */
91  	Address getAddress(int index) {
92  		return addressIndexes.get(index);
93  	}
94  
95  	/**
96  	 * Retargets an already resolved address to a different tuple index.
97  	 * <p>
98  	 * This is used by tuple optimizations that collapse or reorder queue entries
99  	 * after initial address resolution.
100 	 * </p>
101 	 *
102 	 * @param address The resolved address to move
103 	 * @param index The new tuple index for the address
104 	 */
105 	void reassignAddress(Address address, int index) {
106 		int previousIndex = address.index();
107 		if (previousIndex >= 0) {
108 			Address current = addressIndexes.get(previousIndex);
109 			if (current == address && previousIndex != index) {
110 				addressIndexes.remove(previousIndex);
111 			}
112 		}
113 		addressIndexes.put(index, address);
114 		address.assignIndex(index);
115 	}
116 
117 	/**
118 	 * Rebuilds the resolved-address index after queue compaction or reordering.
119 	 * <p>
120 	 * Each entry in {@code indexMapping} maps an old tuple index to its new index,
121 	 * or {@code -1} when that tuple was removed entirely.
122 	 * </p>
123 	 *
124 	 * @param indexMapping Old-to-new tuple index mapping produced by an optimizer
125 	 *        pass
126 	 */
127 	void remapIndexes(int[] indexMapping) {
128 		Map<Integer, Address> updated = new HashMap<Integer, Address>();
129 		for (Map.Entry<Integer, Address> entry : addressIndexes.entrySet()) {
130 			int oldIndex = entry.getKey().intValue();
131 			Address address = entry.getValue();
132 			if (oldIndex >= 0 && oldIndex < indexMapping.length) {
133 				int mappedIndex = indexMapping[oldIndex];
134 				if (mappedIndex >= 0) {
135 					address.assignIndex(mappedIndex);
136 					updated.put(mappedIndex, address);
137 				} else {
138 					address.assignIndex(-1);
139 				}
140 			} else {
141 				address.assignIndex(-1);
142 			}
143 		}
144 		addressIndexes.clear();
145 		addressIndexes.putAll(updated);
146 	}
147 }