@@ -63,7 +63,20 @@ abstract class AttributeContainer {
63
63
64
64
/** Returns a frozen AttributeContainer with the same attributes, and a compact representation. */
65
65
@ CheckReturnValue
66
- abstract AttributeContainer freeze ();
66
+ abstract AttributeContainer freeze (Rule rule );
67
+
68
+ /**
69
+ * Returns {@code true} if this container is immutable.
70
+ *
71
+ * <p>Frozen containers optimize for space by omitting storage for non-explicit attribute values
72
+ * that match the {@link Attribute} default. If {@link #getAttributeValue} returns {@code null},
73
+ * the value should be taken from {@link Attribute#getDefaultValue}, even for computed defaults.
74
+ *
75
+ * <p>Mutable containers have no such optimization. During rule creation, this allows for
76
+ * distinguishing whether a computed default (which may depend on other unset attributes) is
77
+ * available.
78
+ */
79
+ abstract boolean isFrozen ();
67
80
68
81
/** Returns an AttributeContainer for holding attributes of the given rule class. */
69
82
static AttributeContainer newMutableInstance (RuleClass ruleClass ) {
@@ -73,20 +86,19 @@ static AttributeContainer newMutableInstance(RuleClass ruleClass) {
73
86
}
74
87
75
88
/** An AttributeContainer to which attributes may be added. */
76
- static final class Mutable extends AttributeContainer {
89
+ private static final class Mutable extends AttributeContainer {
77
90
78
91
// Sparsely populated array of values, indexed by Attribute.index.
79
92
final Object [] values ;
80
- final BitSet explicitAttrs = new BitSet ();
93
+ final BitSet explicitIndices = new BitSet ();
81
94
82
- @ VisibleForTesting
83
95
Mutable (int maxAttrCount ) {
84
96
values = new Object [maxAttrCount ];
85
97
}
86
98
87
99
@ Override
88
100
public boolean isAttributeValueExplicitlySpecified (int attrIndex ) {
89
- return (attrIndex >= 0 ) && explicitAttrs .get (attrIndex );
101
+ return (attrIndex >= 0 ) && explicitIndices .get (attrIndex );
90
102
}
91
103
92
104
/**
@@ -112,23 +124,45 @@ void setAttributeValue(int attrIndex, Object value, boolean explicit) {
112
124
throw new IllegalArgumentException (
113
125
"attribute with index " + attrIndex + " is not valid for rule" );
114
126
}
115
- if (!explicit && explicitAttrs .get (attrIndex )) {
127
+ if (!explicit && explicitIndices .get (attrIndex )) {
116
128
throw new IllegalArgumentException (
117
129
"attribute with index " + attrIndex + " already explicitly set" );
118
130
}
119
131
values [attrIndex ] = value ;
120
132
if (explicit ) {
121
- explicitAttrs .set (attrIndex );
133
+ explicitIndices .set (attrIndex );
122
134
}
123
135
}
124
136
125
137
@ Override
126
- public AttributeContainer freeze () {
127
- if (values .length < 126 ) {
128
- return new Small (values , explicitAttrs );
129
- } else {
130
- return new Large (values , explicitAttrs );
138
+ public AttributeContainer freeze (Rule rule ) {
139
+ BitSet indicesToStore = new BitSet ();
140
+ RuleClass ruleClass = rule .getRuleClassObject ();
141
+
142
+ for (int i = 0 ; i < values .length ; i ++) {
143
+ Object value = values [i ];
144
+ if (value == null ) {
145
+ continue ;
146
+ }
147
+ if (!explicitIndices .get (i )) {
148
+ Attribute attr = ruleClass .getAttribute (i );
149
+ Object defaultValue = attr .getDefaultValue (attr .hasComputedDefault () ? rule : null );
150
+ if (value .equals (defaultValue )) {
151
+ // Non-explicit value matches the attribute's default. Save space by omitting storage.
152
+ continue ;
153
+ }
154
+ }
155
+ indicesToStore .set (i );
131
156
}
157
+
158
+ return values .length < 126
159
+ ? new Small (values , explicitIndices , indicesToStore )
160
+ : new Large (values , explicitIndices , indicesToStore );
161
+ }
162
+
163
+ @ Override
164
+ public boolean isFrozen () {
165
+ return false ;
132
166
}
133
167
134
168
@ Override
@@ -148,24 +182,14 @@ final void setAttributeValue(int attrIndex, Object value, boolean explicit) {
148
182
}
149
183
150
184
@ Override
151
- final AttributeContainer freeze () {
185
+ final AttributeContainer freeze (Rule rule ) {
152
186
return this ;
153
187
}
154
- }
155
-
156
- private static final byte [] EMPTY_STATE = {};
157
- private static final Object [] EMPTY_VALUES = {};
158
188
159
- /** Returns number of non-null values. */
160
- private static int nonNullCount (Object [] attrValues ) {
161
- // Pre-allocate longer array.
162
- int numSet = 0 ;
163
- for (Object val : attrValues ) {
164
- if (val != null ) {
165
- numSet ++;
166
- }
189
+ @ Override
190
+ final boolean isFrozen () {
191
+ return true ;
167
192
}
168
- return numSet ;
169
193
}
170
194
171
195
/** Returns index into state array for attrIndex, or -1 if not found */
@@ -209,7 +233,7 @@ static final class Small extends Frozen {
209
233
210
234
// The 'value' and 'explicit' components are encoded in the same byte.
211
235
// Since this class only supports ruleClass with < 126 attributes,
212
- // state[i] encodes the the 'value' index in the 7 lower bits and 'explicit' in the top bit.
236
+ // state[i] encodes the 'value' index in the 7 lower bits and 'explicit' in the top bit.
213
237
// This is the common case.
214
238
private final byte [] state ;
215
239
@@ -222,33 +246,27 @@ static final class Small extends Frozen {
222
246
/**
223
247
* Creates a container for a rule of the given rule class. Assumes attrIndex < 126 always.
224
248
*
225
- * @param attrValues values for all attributes, null values are considered unset.
226
- * @param explicitAttrs holds explicit bit for each attribute index
249
+ * @param attrValues values for all attributes, null values are considered unset
250
+ * @param explicitIndices holds explicit bit for each attribute index
251
+ * @param indicesToStore attribute indices for values that need to be stored, i.e., they were
252
+ * explicitly set and/or differ from the attribute's default value
227
253
*/
228
- private Small (Object [] attrValues , BitSet explicitAttrs ) {
229
- maxAttrCount = attrValues .length ;
230
- int numSet = nonNullCount (attrValues );
231
- if (numSet == 0 ) {
232
- this .values = EMPTY_VALUES ;
233
- this .state = EMPTY_STATE ;
234
- return ;
235
- }
236
- values = new Object [numSet ];
237
- state = new byte [numSet ];
238
- int index = 0 ;
239
- int attrIndex = -1 ;
240
- for (Object attrValue : attrValues ) {
241
- attrIndex ++;
242
- if (attrValue == null ) {
243
- continue ;
244
- }
254
+ private Small (Object [] attrValues , BitSet explicitIndices , BitSet indicesToStore ) {
255
+ this .maxAttrCount = attrValues .length ;
256
+ int numToStore = indicesToStore .cardinality ();
257
+ this .values = new Object [numToStore ];
258
+ this .state = new byte [numToStore ];
259
+
260
+ int attrIndex = 0 ;
261
+ for (int i = 0 ; i < numToStore ; i ++) {
262
+ attrIndex = indicesToStore .nextSetBit (attrIndex );
245
263
byte stateValue = (byte ) (0x7f & attrIndex );
246
- if (explicitAttrs .get (attrIndex )) {
264
+ if (explicitIndices .get (attrIndex )) {
247
265
stateValue = (byte ) (stateValue | 0x80 );
248
266
}
249
- state [index ] = stateValue ;
250
- values [index ] = attrValue ;
251
- index += 1 ;
267
+ state [i ] = stateValue ;
268
+ values [i ] = attrValues [ attrIndex ] ;
269
+ attrIndex ++ ;
252
270
}
253
271
}
254
272
@@ -353,32 +371,26 @@ private static boolean getExplicitBit(byte[] bytes, int attrIndex) {
353
371
* Creates a container for a rule of the given rule class. Assumes maxAttrCount < 254
354
372
*
355
373
* @param attrValues values for all attributes, null values are considered unset.
356
- * @param explicitAttrs holds explicit bit for each attribute index
374
+ * @param explicitIndices holds explicit bit for each attribute index
375
+ * @param indicesToStore attribute indices for values that need to be stored, i.e. they were
376
+ * explicitly set and/or differ from the attribute's default value
357
377
*/
358
- private Large (Object [] attrValues , BitSet explicitAttrs ) {
378
+ private Large (Object [] attrValues , BitSet explicitIndices , BitSet indicesToStore ) {
359
379
this .maxAttrCount = attrValues .length ;
360
- int numSet = nonNullCount (attrValues );
361
- if (numSet == 0 ) {
362
- this .values = EMPTY_VALUES ;
363
- this .state = EMPTY_STATE ;
364
- return ;
365
- }
380
+ int numToStore = indicesToStore .cardinality ();
366
381
int p = prefixSize (maxAttrCount );
367
- values = new Object [numSet ];
368
- state = new byte [p + numSet ];
369
- int index = 0 ;
370
- int attrIndex = -1 ;
371
- for (Object attrValue : attrValues ) {
372
- attrIndex ++;
373
- if (attrValue == null ) {
374
- continue ;
375
- }
376
- if (explicitAttrs .get (attrIndex )) {
382
+ this .values = new Object [numToStore ];
383
+ this .state = new byte [p + numToStore ];
384
+
385
+ int attrIndex = 0 ;
386
+ for (int i = 0 ; i < numToStore ; i ++) {
387
+ attrIndex = indicesToStore .nextSetBit (attrIndex );
388
+ if (explicitIndices .get (attrIndex )) {
377
389
setExplicitBit (state , attrIndex );
378
390
}
379
- state [index + p ] = (byte ) attrIndex ;
380
- values [index ] = attrValue ;
381
- index += 1 ;
391
+ state [i + p ] = (byte ) attrIndex ;
392
+ values [i ] = attrValues [ attrIndex ] ;
393
+ attrIndex ++ ;
382
394
}
383
395
}
384
396
0 commit comments