All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datakernel.http.CaseInsensitiveTokenMap Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 SoftIndex LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.datakernel.http;

import io.datakernel.http.CaseInsensitiveTokenMap.Token;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;

import static io.datakernel.bytebuf.ByteBufStrings.*;

/**
 * This is a case-insensitive token registry used as permanent header value cache.
 * Its purpose is to map header values to references to Java objects with maximum efficiency and speed.
 */
public final class CaseInsensitiveTokenMap {
	public static abstract class Token {
		protected final byte[] lowerCaseBytes;
		protected final int lowerCaseHashCode;

		protected Token(@Nullable byte[] lowerCaseBytes, int lowerCaseHashCode) {
			this.lowerCaseBytes = lowerCaseBytes;
			this.lowerCaseHashCode = lowerCaseHashCode;
		}
	}

	protected final T[] TOKENS;
	protected final int maxProbings;
	private final TokenFactory factory;

	protected CaseInsensitiveTokenMap(int slotsNumber, int maxProbings, Class elementsType, TokenFactory factory) {
		this.maxProbings = maxProbings;
		this.factory = factory;
		@SuppressWarnings("unchecked") T[] ts = (T[]) Array.newInstance(elementsType, slotsNumber);
		TOKENS = ts;
	}

	/**
	 * Creates a token with given value and places it in the registry, also returning it.
	 */
	public final T register(String name) {
		assert Integer.bitCount(TOKENS.length) == 1;

		T token = create(name);

		for (int p = 0; p < maxProbings; p++) {
			int slot = (token.lowerCaseHashCode + p) & (TOKENS.length - 1);
			if (TOKENS[slot] == null) {
				TOKENS[slot] = token;
				return token;
			}
		}
		throw new IllegalArgumentException("CaseInsensitiveTokenMap hash collision, try to increase size");
	}

	/**
	 * Creates a token with given value but does not place it into the registry.
	 */
	public final T create(String name) {
		byte[] bytes = encodeAscii(name);

		byte[] lowerCaseBytes = new byte[bytes.length];
		int lowerCaseHashCode = 1;
		for (int i = 0; i < bytes.length; i++) {
			byte b = bytes[i];
			if (b >= 'A' && b <= 'Z') {
				b += 'a' - 'A';
			}
			lowerCaseBytes[i] = b;
			lowerCaseHashCode = lowerCaseHashCode * 31 + b;
		}

		return factory.create(bytes, 0, bytes.length, lowerCaseBytes, lowerCaseHashCode);
	}

	/**
	 * @see #getOrCreate(byte[], int, int, int)
	 */
	public final T getOrCreate(byte[] bytes, int offset, int length) {
		int lowerCaseHashCode = hashCodeLowerCaseAscii(bytes, offset, length);
		return getOrCreate(bytes, offset, length, lowerCaseHashCode);
	}

	/**
	 * Gets the cached token value for given input, or creates a new one without putting it in the registry.
	 */
	public final T getOrCreate(byte[] bytes, int offset, int length, int lowerCaseHashCode) {
		T t = get(bytes, offset, length, lowerCaseHashCode);
		return (t != null) ? t : factory.create(bytes, offset, length, null, lowerCaseHashCode);
	}

	/**
	 * Returns registered token value for given input, or null if no such value was registered.
	 * This methid is very fast.
	 */
	public final T get(byte[] bytes, int offset, int length, int lowerCaseHashCode) {
		for (int p = 0; p < maxProbings; p++) {
			int slot = (lowerCaseHashCode + p) & (TOKENS.length - 1);
			T t = TOKENS[slot];
			if (t == null) {
				break;
			}
			if (t.lowerCaseHashCode == lowerCaseHashCode && equalsLowerCaseAscii(t.lowerCaseBytes, bytes, offset, length)) {
				return t;
			}
		}
		return null;
	}

	@FunctionalInterface
	public interface TokenFactory {

		T create(byte[] bytes, int offset, int length, byte[] lowerCaseBytes, int lowerCaseHashCode);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy