//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
Abstract Trie implementation.
Provides some common implementations, which may not be the most
efficient. For byte operations, the assumption is made that the charset
is ISO-8859-1
Type parameters: - <V> – the type of object that the Trie holds
/**
* Abstract Trie implementation.
* <p>Provides some common implementations, which may not be the most
* efficient. For byte operations, the assumption is made that the charset
* is ISO-8859-1</p>
*
* @param <V> the type of object that the Trie holds
*/
abstract class AbstractTrie<V> implements Index.Mutable<V>
{
final boolean _caseInsensitive;
protected AbstractTrie(boolean insensitive)
{
_caseInsensitive = insensitive;
}
public boolean isCaseInsensitive()
{
return _caseInsensitive;
}
public boolean put(V v)
{
return put(v.toString(), v);
}
public V remove(String s)
{
V o = get(s);
put(s, null);
return o;
}
public V get(String s)
{
return get(s, 0, s.length());
}
public V get(ByteBuffer b)
{
return get(b, 0, b.remaining());
}
public V getBest(String s)
{
return getBest(s, 0, s.length());
}
public V getBest(byte[] b, int offset, int len)
{
return getBest(new String(b, offset, len, StandardCharsets.ISO_8859_1));
}
Calculate required Trie capacity in nodes of a tree decomposition of the keys.
For example given the keys:
- utf_16
- utf_8
- utf16
- utf8
The tree has 10 nodes as follows:
1 - 6
/
_ - 8
/
u - t - f - 1 - 6
\
8
Params: - keys – The keys to be put in a Trie
- caseSensitive – true if the capacity should be calculated with case-sensitive keys
Returns: The capacity in nodes of a tree decomposition
/**
* Calculate required Trie capacity in nodes of a tree decomposition of the keys.
* For example given the keys:
* <ul>
* <li>utf_16</li>
* <li>utf_8</li>
* <li>utf16</li>
* <li>utf8</li>
* </ul>
* The tree has 10 nodes as follows:
* <pre>
* 1 - 6
* /
* _ - 8
* /
* u - t - f - 1 - 6
* \
* 8
* </pre>
* @param keys The keys to be put in a Trie
* @param caseSensitive true if the capacity should be calculated with case-sensitive keys
* @return The capacity in nodes of a tree decomposition
*/
protected static int requiredCapacity(Set<String> keys, boolean caseSensitive)
{
List<String> list = caseSensitive
? new ArrayList<>(keys)
: keys.stream().map(String::toLowerCase).collect(Collectors.toList());
Collections.sort(list);
return AbstractTrie.requiredCapacity(list, 0, list.size(), 0);
}
Calculate required Trie capacity in nodes of a sub-tree decomposition of the keys.
Params: - keys – The keys to calculate the capacity for
- offset – The offset of the first key to be considered
- length – The number of keys to be considered
- index – The character to be considered
Returns: The capacity in tree nodes of the substree
/**
* Calculate required Trie capacity in nodes of a sub-tree decomposition of the keys.
* @param keys The keys to calculate the capacity for
* @param offset The offset of the first key to be considered
* @param length The number of keys to be considered
* @param index The character to be considered
* @return The capacity in tree nodes of the substree
*/
private static int requiredCapacity(List<String> keys, int offset, int length, int index)
{
int required = 0;
// Examine all the keys in the subtree
Character nodeChar = null;
for (int i = 0; i < length; i++)
{
String k = keys.get(offset + i);
// If the key is shorter than our current index then ignore it
if (k.length() <= index)
continue;
// Get the character at the index of the current key
char c = k.charAt(index);
// If the character is the same as the current node, then we are
// still in the current node and need to continue searching for the
// next node or the end of the keys
if (nodeChar != null && c == nodeChar)
continue;
// The character is a new node, so increase required by 1
required++;
// if we had a previous node, then add the required nodes for the subtree under it.
if (nodeChar != null)
required += AbstractTrie.requiredCapacity(keys, offset, i, index + 1);
// set the char for the new node
nodeChar = c;
// reset the offset, length and index to continue iteration from the start of the new node
offset += i;
length -= i;
i = 0;
}
// If we finish the iteration with a nodeChar, then we must add the required nodes for the subtree under it.
if (nodeChar != null)
required += AbstractTrie.requiredCapacity(keys, offset, length, index + 1);
return required;
}
}