88 *
99 * SPDX-License-Identifier: BSD-3-Clause
1010 *******************************************************************************/
11+ // Some portions generated by Codex
1112package org .eclipse .rdf4j .query .algebra .evaluation .impl ;
1213
1314import java .util .Arrays ;
15+ import java .util .BitSet ;
16+ import java .util .Collections ;
1417import java .util .Comparator ;
1518import java .util .HashMap ;
1619import java .util .HashSet ;
1720import java .util .LinkedHashMap ;
21+ import java .util .LinkedHashSet ;
1822import java .util .List ;
1923import java .util .Set ;
24+ import java .util .concurrent .ConcurrentHashMap ;
2025import java .util .function .BiConsumer ;
2126import java .util .function .Function ;
27+ import java .util .function .LongFunction ;
2228import java .util .function .Predicate ;
2329import java .util .stream .Collectors ;
2430
4854import org .eclipse .rdf4j .query .algebra .helpers .AbstractSimpleQueryModelVisitor ;
4955import org .eclipse .rdf4j .query .impl .EmptyBindingSet ;
5056
51- public final class ArrayBindingBasedQueryEvaluationContext implements QueryEvaluationContext {
57+ public final class ArrayBindingBasedQueryEvaluationContext
58+ implements QueryEvaluationContext , ArrayBindingSet .BindingNamesCache {
5259
5360 public static final Predicate <BindingSet > HAS_BINDING_FALSE = (bs ) -> false ;
5461 public static final Function <BindingSet , Binding > GET_BINDING_NULL = (bs ) -> null ;
@@ -68,6 +75,12 @@ public final class ArrayBindingBasedQueryEvaluationContext implements QueryEvalu
6875 private final BiConsumer <Value , MutableBindingSet >[] addBinding ;
6976 private final Comparator <Value > comparator ;
7077
78+ private final LongFunction <Set <String >> bindingNamesFromLongMask = this ::toBindingNamesSetFromLongMask ;
79+ private final Function <BitSet , Set <String >> bindingNamesFromBitSet = this ::toBindingNamesSetFromBitSet ;
80+
81+ private final LongKeyCache <Set <String >> bindingNamesCacheLong = new LongKeyCache <>();
82+ private final ConcurrentHashMap <BitSet , Set <String >> bindingNamesCacheBitSet = new ConcurrentHashMap <>();
83+
7184 private final boolean initialized ;
7285
7386 @ InternalUseOnly
@@ -77,7 +90,7 @@ public ArrayBindingBasedQueryEvaluationContext(QueryEvaluationContext context, S
7790 this .context = context ;
7891 this .allVariables = allVariables ;
7992 this .allVariablesSet = Set .of (allVariables );
80- this .defaultArrayBindingSet = new ArrayBindingSet (allVariables );
93+ this .defaultArrayBindingSet = new ArrayBindingSet (this , allVariables );
8194 this .comparator = comparator ;
8295
8396 hasBinding = new Predicate [allVariables .length ];
@@ -115,7 +128,7 @@ public Dataset getDataset() {
115128
116129 @ Override
117130 public ArrayBindingSet createBindingSet () {
118- return new ArrayBindingSet (allVariables );
131+ return new ArrayBindingSet (this , allVariables );
119132 }
120133
121134 @ Override
@@ -329,14 +342,139 @@ public BiConsumer<Value, MutableBindingSet> addBinding(String variableName) {
329342 @ Override
330343 public ArrayBindingSet createBindingSet (BindingSet bindings ) {
331344 if (bindings instanceof ArrayBindingSet ) {
332- return new ArrayBindingSet ((ArrayBindingSet ) bindings , allVariables );
345+ return new ArrayBindingSet (this , (ArrayBindingSet ) bindings , allVariables );
333346 } else if (bindings == EmptyBindingSet .getInstance ()) {
334347 return createBindingSet ();
335348 } else {
336- return new ArrayBindingSet (bindings , allVariablesSet , allVariables );
349+ return new ArrayBindingSet (this , bindings , allVariablesSet , allVariables );
337350 }
338351 }
339352
353+ @ Override
354+ public Set <String > getBindingNames (long presentMask ) {
355+ return bindingNamesCacheLong .getOrCompute (presentMask , bindingNamesFromLongMask );
356+ }
357+
358+ @ Override
359+ public Set <String > getBindingNames (BitSet presentMask ) {
360+ return bindingNamesCacheBitSet .computeIfAbsent (presentMask , bindingNamesFromBitSet );
361+ }
362+
363+ private Set <String > toBindingNamesSetFromLongMask (long presentMask ) {
364+ int size = Long .bitCount (presentMask );
365+ if (size == 0 ) {
366+ return Collections .emptySet ();
367+ }
368+ if (size == 1 ) {
369+ return Collections .singleton (allVariables [Long .numberOfTrailingZeros (presentMask )]);
370+ }
371+
372+ LinkedHashSet <String > set = new LinkedHashSet <>(size * 2 );
373+ for (long bits = presentMask ; bits != 0 ; bits &= (bits - 1 )) {
374+ int index = Long .numberOfTrailingZeros (bits );
375+ set .add (allVariables [index ]);
376+ }
377+ return Collections .unmodifiableSet (set );
378+ }
379+
380+ private Set <String > toBindingNamesSetFromBitSet (BitSet presentMask ) {
381+ int size = presentMask .cardinality ();
382+ if (size == 0 ) {
383+ return Collections .emptySet ();
384+ }
385+ if (size == 1 ) {
386+ return Collections .singleton (allVariables [presentMask .nextSetBit (0 )]);
387+ }
388+
389+ LinkedHashSet <String > set = new LinkedHashSet <>(size * 2 );
390+ for (int index = presentMask .nextSetBit (0 ); index >= 0 ; index = presentMask .nextSetBit (index + 1 )) {
391+ set .add (allVariables [index ]);
392+ }
393+ return Collections .unmodifiableSet (set );
394+ }
395+
396+ private static final class LongKeyCache <V > {
397+
398+ private static final int INITIAL_CAPACITY = 16 ;
399+ private static final float LOAD_FACTOR = 0.6f ;
400+
401+ private long [] keys = new long [INITIAL_CAPACITY ];
402+ private Object [] values = new Object [INITIAL_CAPACITY ];
403+ private int size = 0 ;
404+ private int resizeThreshold = (int ) (INITIAL_CAPACITY * LOAD_FACTOR );
405+
406+ public synchronized V getOrCompute (long key , LongFunction <? extends V > compute ) {
407+ V existing = get (key );
408+ if (existing != null ) {
409+ return existing ;
410+ }
411+
412+ if (size >= resizeThreshold ) {
413+ resize ();
414+ }
415+
416+ V created = compute .apply (key );
417+ if (created == null ) {
418+ throw new NullPointerException ("value" );
419+ }
420+ insert (key , created );
421+ return created ;
422+ }
423+
424+ private V get (long key ) {
425+ int index = hash (key ) & (values .length - 1 );
426+ while (true ) {
427+ Object value = values [index ];
428+ if (value == null ) {
429+ return null ;
430+ }
431+ if (keys [index ] == key ) {
432+ return (V ) value ;
433+ }
434+ index = (index + 1 ) & (values .length - 1 );
435+ }
436+ }
437+
438+ private void insert (long key , V value ) {
439+ int index = hash (key ) & (values .length - 1 );
440+ while (values [index ] != null ) {
441+ index = (index + 1 ) & (values .length - 1 );
442+ }
443+ keys [index ] = key ;
444+ values [index ] = value ;
445+ size ++;
446+ }
447+
448+ private void resize () {
449+ long [] oldKeys = keys ;
450+ Object [] oldValues = values ;
451+
452+ keys = new long [oldKeys .length * 2 ];
453+ values = new Object [oldValues .length * 2 ];
454+ size = 0 ;
455+ resizeThreshold = (int ) (values .length * LOAD_FACTOR );
456+
457+ for (int i = 0 ; i < oldValues .length ; i ++) {
458+ Object oldValue = oldValues [i ];
459+ if (oldValue == null ) {
460+ continue ;
461+ }
462+ insert (oldKeys [i ], (V ) oldValue );
463+ }
464+ }
465+
466+ private static int hash (long key ) {
467+ long h = key ;
468+ h ^= (h >>> 33 );
469+ h *= 0xff51afd7ed558ccdL ;
470+ h ^= (h >>> 33 );
471+ h *= 0xc4ceb9fe1a85ec53L ;
472+ h ^= (h >>> 33 );
473+ return (int ) h ;
474+ }
475+
476+ }
477+
340478 public static String [] findAllVariablesUsedInQuery (QueryRoot node ) {
341479 HashMap <String , String > varNames = new LinkedHashMap <>();
342480 AbstractSimpleQueryModelVisitor <QueryEvaluationException > queryModelVisitorBase = new AbstractSimpleQueryModelVisitor <>(
0 commit comments