1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3.internal;
18
19 import java.util.BitSet;
20
21 /**
22 * The set of symbols declared in a lexical scope.
23 * <p>The symbol identifiers are determined by the functional scope.</p>
24 * <p>We use 2 bits per symbol s; bit (s*2)+0 sets the actual symbol as lexical (let), bit (s*2)+1 as a const.
25 * There are actually only 2 used states: 1 and 3</p>
26 */
27 public class LexicalScope {
28 /**
29 * Number of bits in a long.
30 */
31 protected static final int BITS_PER_LONG = 64;
32 /**
33 * Bits per symbol.
34 * let (b + 0) + const (b + 1).
35 */
36 protected static final int BITS_PER_SYMBOL = 2;
37 /**
38 * From a symbol number to a starting symbol bit number.
39 */
40 protected static final int SYMBOL_SHIFT = BITS_PER_SYMBOL - 1;
41 /**
42 * Bitmask for symbols.
43 */
44 protected static final long SYMBOL_MASK = (1L << BITS_PER_SYMBOL - 1) - 1; // 3, as 1+2, 2 bits
45 /**
46 * Number of symbols.
47 */
48 protected int count;
49 /**
50 * The mask of symbols in the scope.
51 */
52 protected long symbols;
53 /**
54 * Symbols after bit 64 (aka symbol 32 when 2 bits per symbol).
55 */
56 protected BitSet moreSymbols;
57
58 /**
59 * Create a scope.
60 */
61 public LexicalScope() {
62 }
63
64 /**
65 * Frame copy ctor base.
66 */
67 protected LexicalScope(final LexicalScope other) {
68 this.symbols = other.symbols;
69 final BitSet otherMoreSymbols = other.moreSymbols;
70 this.moreSymbols = otherMoreSymbols != null ? (BitSet) otherMoreSymbols.clone() : null;
71 this.count = other.count;
72 }
73
74 /**
75 * Adds a constant in this scope.
76 *
77 * @param symbol the symbol
78 * @return true if registered, false if symbol was already registered
79 */
80 public boolean addConstant(final int symbol) {
81 final int letb = symbol << SYMBOL_SHIFT ;
82 if (!isSet(letb)) {
83 throw new IllegalStateException("const not declared as symbol " + symbol);
84 }
85 final int bit = symbol << SYMBOL_SHIFT | 1;
86 return set(bit);
87 }
88
89 /**
90 * Adds a symbol in this scope.
91 *
92 * @param symbol the symbol
93 * @return true if registered, false if symbol was already registered
94 */
95 public boolean addSymbol(final int symbol) {
96 final int bit = symbol << SYMBOL_SHIFT ;
97 if (set(bit)) {
98 count += 1;
99 return true;
100 }
101 return false;
102 }
103
104 /**
105 * Clear all symbols.
106 *
107 * @param cleanSymbol a (optional, may be null) functor to call for each cleaned symbol
108 */
109 public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol) {
110 // undefine symbols getting out of scope
111 if (cleanSymbol != null) {
112 long clean = symbols;
113 while (clean != 0L) {
114 final int bit = Long.numberOfTrailingZeros(clean);
115 final int s = bit >> SYMBOL_SHIFT;
116 cleanSymbol.accept(s);
117 // call clean for symbol definition (3 as a mask for 2 bits,1+2)
118 clean &= ~(SYMBOL_MASK << bit);
119 }
120 // step by bits per symbol
121 int bit = moreSymbols != null ? moreSymbols.nextSetBit(0) : -1;
122 while (bit >= 0) {
123 final int s = bit + BITS_PER_LONG >> SYMBOL_SHIFT;
124 cleanSymbol.accept(s);
125 bit = moreSymbols.nextSetBit(bit + BITS_PER_SYMBOL);
126 }
127 }
128 // internal cleansing
129 symbols = 0L;
130 count = 0;
131 if (moreSymbols != null) {
132 moreSymbols.clear();
133 }
134 }
135
136 /**
137 * @return the number of symbols defined in this scope.
138 */
139 public int getSymbolCount() {
140 return count;
141 }
142
143 /**
144 * Checks whether a symbol has already been declared.
145 *
146 * @param symbol the symbol
147 * @return true if declared, false otherwise
148 */
149 public boolean hasSymbol(final int symbol) {
150 final int bit = symbol << SYMBOL_SHIFT;
151 return isSet(bit);
152 }
153
154 /**
155 * Checks whether a symbol is declared as a constant.
156 *
157 * @param symbol the symbol
158 * @return true if declared as constant, false otherwise
159 */
160 public boolean isConstant(final int symbol) {
161 final int bit = symbol << SYMBOL_SHIFT | 1;
162 return isSet(bit);
163 }
164
165 /**
166 * Whether a given bit (not symbol) is set.
167 * @param bit the bit
168 * @return true if set
169 */
170 private boolean isSet(final int bit) {
171 if (bit < BITS_PER_LONG) {
172 return (symbols & 1L << bit) != 0L;
173 }
174 return moreSymbols != null && moreSymbols.get(bit - BITS_PER_LONG);
175 }
176
177 /**
178 * Ensures more symbols can be stored.
179 *
180 * @return the set of more symbols
181 */
182 private BitSet moreBits() {
183 if (moreSymbols == null) {
184 moreSymbols = new BitSet();
185 }
186 return moreSymbols;
187 }
188
189 /**
190 * Sets a given bit (not symbol).
191 * @param bit the bit
192 * @return true if it was actually set, false if it was set before
193 */
194 private boolean set(final int bit) {
195 if (bit < BITS_PER_LONG) {
196 if ((symbols & 1L << bit) != 0L) {
197 return false;
198 }
199 symbols |= 1L << bit;
200 } else {
201 final int bit64 = bit - BITS_PER_LONG;
202 final BitSet ms = moreBits();
203 if (ms.get(bit64)) {
204 return false;
205 }
206 ms.set(bit64, true);
207 }
208 return true;
209 }
210 }