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 }