Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speedup HTTP headers lowercase transformation #5055

Merged
merged 1 commit into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/main/java/io/vertx/core/http/impl/HttpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,18 +704,32 @@ static String getWebSocketLocation(HttpServerRequest req, boolean ssl) throws Ex
}

/**
* This is optimized for Http headers, which are usually in the form:<br>
* eg Content-Length, content-length
*
* @return convert the {@code sequence} to a lower case instance
*/
public static CharSequence toLowerCase(CharSequence sequence) {
StringBuilder buffer = null;
if (sequence instanceof AsciiString) {
return ((AsciiString) sequence).toLowerCase();
}
return toCharSequenceLowerCase(sequence);
}

private static CharSequence toCharSequenceLowerCase(CharSequence sequence) {
int len = sequence.length();
StringBuilder buffer = null;
for (int index = 0; index < len; index++) {
char c = sequence.charAt(index);
if (c >= 'A' && c <= 'Z') {
if (buffer == null) {
if (sequence instanceof String) {
return toLowerCase(((String) sequence).toCharArray(), index, len);
}
// just copy the lowercase part
buffer = new StringBuilder(sequence);
}
buffer.setCharAt(index, (char)(c + ('a' - 'A')));
buffer.setCharAt(index, (char) (c + ('a' - 'A')));
}
}
if (buffer != null) {
Expand All @@ -725,6 +739,17 @@ public static CharSequence toLowerCase(CharSequence sequence) {
}
}

private static CharSequence toLowerCase(char[] chars, int firstUpperCase, int length) {
assert chars[firstUpperCase] >= 'A' && chars[firstUpperCase] <= 'Z';
for (int i = firstUpperCase; i < length; i++) {
char c = chars[i];
if (c >= 'A' && c <= 'Z') {
chars[i] = (char) (c + ('a' - 'A'));
}
}
return new String(chars, 0, length == chars.length? chars.length : length);
}

static HttpVersion toNettyHttpVersion(io.vertx.core.http.HttpVersion version) {
switch (version) {
case HTTP_1_0: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/

package io.vertx.benchmarks;

import io.netty.util.AsciiString;
import io.vertx.core.http.impl.HttpUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

@State(Scope.Benchmark)
@Warmup(iterations = 20, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 200, timeUnit = MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(NANOSECONDS)
@Fork(2)
public class HttpUtilsAsciiToLowerCaseBenchmark {

@Param({"ascii", "string", "builder"})
public String type;

@Param({"false", "true"})
public boolean lowerCase;
private CharSequence sequence;

@Setup
public void init() {
sequence = create(type, lowerCase ? "content-length" : "Content-Length");
}

private CharSequence create(String type, String content) {

switch (type) {
case "ascii":
return new AsciiString(content);
case "string":
return content;
case "builder":
return new StringBuilder(content);
default:
throw new IllegalStateException();
}
}

@Benchmark
public CharSequence toLowerCase() {
return HttpUtils.toLowerCase(sequence);
}

}
Loading