-
Notifications
You must be signed in to change notification settings - Fork 179
Headers +toString(), fixes #1385
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7da6682
Headers: toString! fix Nullability; use entrySet instead of keySet+ge…
magicprinc 13d38d0
Merge branch 'main' into feature/Headers
scottf b13a224
revert readOnly back to field
magicprinc cd4f4d2
refactoring after review
magicprinc 2475934
much faster serializeToArray
magicprinc 19122d0
benchmark: new serializeToArray is 3.7 times faster
magicprinc 6916871
disable benchmark
magicprinc d110801
Merge branch 'nats-io:main' into feature/Headers
magicprinc 5893a7a
simplify validation message after review
magicprinc 3788bb1
simplify toString after review
magicprinc bfdb487
Merge branch 'main' into feature/Headers
scottf e23a805
Merge branch 'main' into feature/Headers
scottf 5e682c7
optimize Headers.Checker after review https://github.com/nats-io/nats…
magicprinc 29e3088
fix after review
magicprinc 9f528a2
Merge branch 'main' into feature/Headers
scottf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,11 @@ | |
package io.nats.client.impl; | ||
|
||
import io.nats.client.support.ByteArrayBuilder; | ||
import org.jspecify.annotations.Nullable; | ||
|
||
import java.util.*; | ||
import java.util.function.BiConsumer; | ||
import java.util.stream.Collectors; | ||
|
||
import static io.nats.client.support.NatsConstants.*; | ||
import static java.nio.charset.StandardCharsets.US_ASCII; | ||
|
@@ -36,8 +38,7 @@ public class Headers { | |
|
||
private final Map<String, List<String>> valuesMap; | ||
private final Map<String, Integer> lengthMap; | ||
private final boolean readOnly; | ||
private byte[] serialized; | ||
private byte @Nullable [] serialized; | ||
private int dataLength; | ||
|
||
public Headers() { | ||
|
@@ -52,7 +53,7 @@ public Headers(Headers headers, boolean readOnly) { | |
this(headers, readOnly, null); | ||
} | ||
|
||
public Headers(Headers headers, boolean readOnly, String[] keysNotToCopy) { | ||
public Headers(@Nullable Headers headers, boolean readOnly, String @Nullable [] keysNotToCopy) { | ||
Map<String, List<String>> tempValuesMap = new HashMap<>(); | ||
Map<String, Integer> tempLengthMap = new HashMap<>(); | ||
if (headers != null) { | ||
|
@@ -69,7 +70,6 @@ public Headers(Headers headers, boolean readOnly, String[] keysNotToCopy) { | |
} | ||
} | ||
} | ||
this.readOnly = readOnly; | ||
if (readOnly) { | ||
valuesMap = Collections.unmodifiableMap(tempValuesMap); | ||
lengthMap = Collections.unmodifiableMap(tempLengthMap); | ||
|
@@ -92,7 +92,7 @@ public Headers(Headers headers, boolean readOnly, String[] keysNotToCopy) { | |
* -or- if any value contains invalid characters | ||
*/ | ||
public Headers add(String key, String... values) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new UnsupportedOperationException(); | ||
} | ||
if (values == null || values.length == 0) { | ||
|
@@ -113,7 +113,7 @@ public Headers add(String key, String... values) { | |
* -or- if any value contains invalid characters | ||
*/ | ||
public Headers add(String key, Collection<String> values) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new UnsupportedOperationException(); | ||
} | ||
if (values == null || values.isEmpty()) { | ||
|
@@ -153,7 +153,7 @@ private Headers _add(String key, Collection<String> values) { | |
* -or- if any value contains invalid characters | ||
*/ | ||
public Headers put(String key, String... values) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new UnsupportedOperationException(); | ||
} | ||
if (values == null || values.length == 0) { | ||
|
@@ -174,7 +174,7 @@ public Headers put(String key, String... values) { | |
* -or- if any value contains invalid characters | ||
*/ | ||
public Headers put(String key, Collection<String> values) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new UnsupportedOperationException(); | ||
} | ||
if (values == null || values.isEmpty()) { | ||
|
@@ -191,14 +191,14 @@ public Headers put(String key, Collection<String> values) { | |
* @return the Headers object | ||
*/ | ||
public Headers put(Map<String, List<String>> map) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new UnsupportedOperationException(); | ||
} | ||
if (map == null || map.isEmpty()) { | ||
return this; | ||
} | ||
for (String key : map.keySet() ) { | ||
_put(key, map.get(key)); | ||
for (Map.Entry<String, List<String>> entry : map.entrySet()) { | ||
_put(entry.getKey(), entry.getValue()); | ||
scottf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return this; | ||
} | ||
|
@@ -228,7 +228,7 @@ private Headers _put(String key, Collection<String> values) { | |
* @param keys the key or keys to remove | ||
*/ | ||
public void remove(String... keys) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert |
||
throw new UnsupportedOperationException(); | ||
} | ||
for (String key : keys) { | ||
|
@@ -243,7 +243,7 @@ public void remove(String... keys) { | |
* @param keys the key or keys to remove | ||
*/ | ||
public void remove(Collection<String> keys) { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert |
||
throw new UnsupportedOperationException(); | ||
} | ||
for (String key : keys) { | ||
|
@@ -282,7 +282,7 @@ public boolean isEmpty() { | |
* Removes all the keys The object map will be empty after this call returns. | ||
*/ | ||
public void clear() { | ||
if (readOnly) { | ||
if (isReadOnly()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert |
||
throw new UnsupportedOperationException(); | ||
} | ||
valuesMap.clear(); | ||
|
@@ -331,7 +331,7 @@ public Set<String> keySet() { | |
* @return a read-only set of keys (in lowercase) contained in this map | ||
*/ | ||
public Set<String> keySetIgnoreCase() { | ||
HashSet<String> set = new HashSet<>(); | ||
HashSet<String> set = new HashSet<>(valuesMap.size()*4/3 + 1); | ||
scottf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (String k : valuesMap.keySet()) { | ||
set.add(k.toLowerCase()); | ||
} | ||
|
@@ -345,7 +345,7 @@ public Set<String> keySetIgnoreCase() { | |
* @param key the key whose associated value is to be returned | ||
* @return a read-only list of the values for the case-sensitive key. | ||
*/ | ||
public List<String> get(String key) { | ||
public @Nullable List<String> get(String key) { | ||
List<String> values = valuesMap.get(key); | ||
return values == null ? null : Collections.unmodifiableList(values); | ||
} | ||
|
@@ -356,7 +356,7 @@ public List<String> get(String key) { | |
* @param key the key whose associated value is to be returned | ||
* @return the first value for the case-sensitive key. | ||
*/ | ||
public String getFirst(String key) { | ||
public @Nullable String getFirst(String key) { | ||
List<String> values = valuesMap.get(key); | ||
return values == null ? null : values.get(0); | ||
} | ||
|
@@ -368,7 +368,7 @@ public String getFirst(String key) { | |
* @param key the key whose associated value is to be returned | ||
* @return the last value for the case-sensitive key. | ||
*/ | ||
public String getLast(String key) { | ||
public @Nullable String getLast(String key) { | ||
List<String> values = valuesMap.get(key); | ||
return values == null ? null : values.get(values.size() - 1); | ||
} | ||
|
@@ -380,11 +380,11 @@ public String getLast(String key) { | |
* @param key the key whose associated value is to be returned | ||
* @return a read-only list of the values for the case-insensitive key. | ||
*/ | ||
public List<String> getIgnoreCase(String key) { | ||
public @Nullable List<String> getIgnoreCase(String key) { | ||
List<String> values = new ArrayList<>(); | ||
for (String k : valuesMap.keySet()) { | ||
if (k.equalsIgnoreCase(key)) { | ||
values.addAll(valuesMap.get(k)); | ||
for (Map.Entry<String, List<String>> entry : valuesMap.entrySet()) { | ||
if (entry.getKey().equalsIgnoreCase(key)) { | ||
values.addAll(entry.getValue()); | ||
} | ||
} | ||
return values.isEmpty() ? null : Collections.unmodifiableList(values); | ||
|
@@ -401,7 +401,8 @@ public List<String> getIgnoreCase(String key) { | |
* removed during iteration | ||
*/ | ||
public void forEach(BiConsumer<String, List<String>> action) { | ||
Collections.unmodifiableMap(valuesMap).forEach(action); | ||
valuesMap.forEach((key, values) -> | ||
action.accept(key, Collections.unmodifiableList(values))); | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
|
@@ -460,9 +461,9 @@ public byte[] getSerialized() { | |
@Deprecated | ||
public ByteArrayBuilder appendSerialized(ByteArrayBuilder bab) { | ||
bab.append(HEADER_VERSION_BYTES_PLUS_CRLF); | ||
for (String key : valuesMap.keySet()) { | ||
for (String value : valuesMap.get(key)) { | ||
bab.append(key); | ||
for (Map.Entry<String, List<String>> entry : valuesMap.entrySet()) { | ||
for (String value : entry.getValue()) { | ||
bab.append(entry.getKey()); | ||
bab.append(COLON_BYTES); | ||
bab.append(value); | ||
bab.append(CRLF_BYTES); | ||
|
@@ -535,11 +536,12 @@ private void checkKey(String key) { | |
private void checkValue(String val) { | ||
// Like rfc822 section 3.1.2 (quoted in ADR 4) | ||
// The field-body may be composed of any US-ASCII characters, except CR or LF. | ||
val.chars().forEach(c -> { | ||
for (int i = 0, len = val.length(); i < len; i++) { | ||
int c = val.charAt(i); | ||
if (c > 127 || c == 10 || c == 13) { | ||
throw new IllegalArgumentException(VALUE_INVALID_CHARACTERS + c); | ||
throw new IllegalArgumentException(VALUE_INVALID_CHARACTERS + Integer.toHexString(c)); | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}); | ||
} | ||
} | ||
|
||
private class Checker { | ||
|
@@ -575,19 +577,34 @@ boolean hasValues() { | |
* @return the read only state | ||
*/ | ||
public boolean isReadOnly() { | ||
return readOnly; | ||
return !(valuesMap instanceof HashMap); | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
if (!(o instanceof Headers)) return false; | ||
Headers headers = (Headers) o; | ||
return Objects.equals(valuesMap, headers.valuesMap); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(valuesMap); | ||
return Objects.hashCode(valuesMap); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return valuesMap.entrySet().stream() | ||
.filter(e -> e.getValue() != null) | ||
.sorted(Map.Entry.comparingByKey()) | ||
.map(e -> { | ||
String headerName = e.getKey(); | ||
List<String> values = e.getValue(); | ||
return headerName +": "+ (values.size() == 1 ? values.get(0) | ||
: String.join(", ", values) | ||
); | ||
}) | ||
.collect(Collectors.joining("; ")); | ||
magicprinc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.