/** * Returns set of all keys in store. * * @return unmodifiable Set of keys */ @SuppressWarnings("deprecation") // The String ctor is deprecated, but fast. public Set<String> keys() { if (isEmpty()) { return Collections.emptySet(); } Set<String> ks = new HashSet<String>(size); for (int i = 0; i < size; i++) { ks.add(new String(name(i), 0 /* hibyte */)); } // immutable in case we decide to change the implementation later. return Collections.unmodifiableSet(ks); }
/** * Returns set of all keys in store. * * @return unmodifiable Set of keys */ @SuppressWarnings("deprecation") // The String ctor is deprecated, but fast. public Set<String> keys() { if (isEmpty()) { return Collections.emptySet(); } Set<String> ks = new HashSet<String>(size); for (int i = 0; i < size; i++) { ks.add(new String(name(i), 0 /* hibyte */)); } // immutable in case we decide to change the implementation later. return Collections.unmodifiableSet(ks); }
@SuppressWarnings("BetaApi") // BaseEncoding is stable in Guava 20.0 @Override public String toString() { StringBuilder sb = new StringBuilder("Metadata("); for (int i = 0; i < size; i++) { if (i != 0) { sb.append(','); } String headerName = new String(name(i), US_ASCII); sb.append(headerName).append('='); if (headerName.endsWith(BINARY_HEADER_SUFFIX)) { sb.append(BASE64_ENCODING_OMIT_PADDING.encode(value(i))); } else { String headerValue = new String(value(i), US_ASCII); sb.append(headerValue); } } return sb.append(')').toString(); }
/** * Merge values from the given set of keys into this set of metadata. If a key is present in keys, * then all of the associated values will be copied over. * * @param other The source of the new key values. * @param keys The subset of matching key we want to copy, if they exist in the source. */ public void merge(Metadata other, Set<Key<?>> keys) { Preconditions.checkNotNull(other, "other"); // Use ByteBuffer for equals and hashCode. Map<ByteBuffer, Key<?>> asciiKeys = new HashMap<ByteBuffer, Key<?>>(keys.size()); for (Key<?> key : keys) { asciiKeys.put(ByteBuffer.wrap(key.asciiName()), key); } for (int i = 0; i < other.size; i++) { ByteBuffer wrappedNamed = ByteBuffer.wrap(other.name(i)); if (asciiKeys.containsKey(wrappedNamed)) { maybeExpand(); name(size, other.name(i)); value(size, other.value(i)); size++; } } }
@SuppressWarnings("BetaApi") // BaseEncoding is stable in Guava 20.0 @Override public String toString() { StringBuilder sb = new StringBuilder("Metadata("); for (int i = 0; i < size; i++) { if (i != 0) { sb.append(','); } String headerName = new String(name(i), US_ASCII); sb.append(headerName).append('='); if (headerName.endsWith(BINARY_HEADER_SUFFIX)) { sb.append(BaseEncoding.base64().encode(value(i))); } else { String headerValue = new String(value(i), US_ASCII); sb.append(headerValue); } } return sb.append(')').toString(); }
/** * Returns true if a value is defined for the given key. * * <p>This is done by linear search, so if it is followed by {@link #get} or {@link #getAll}, * prefer calling them directly and checking the return value against {@code null}. */ public boolean containsKey(Key<?> key) { for (int i = 0; i < size; i++) { if (bytesEqual(key.asciiName(), name(i))) { return true; } } return false; }
/** * Returns true if a value is defined for the given key. * * <p>This is done by linear search, so if it is followed by {@link #get} or {@link #getAll}, * prefer calling them directly and checking the return value against {@code null}. */ public boolean containsKey(Key<?> key) { for (int i = 0; i < size; i++) { if (bytesEqual(key.asciiName(), name(i))) { return true; } } return false; }
/** * Merge values from the given set of keys into this set of metadata. If a key is present in keys, * then all of the associated values will be copied over. * * @param other The source of the new key values. * @param keys The subset of matching key we want to copy, if they exist in the source. */ public void merge(Metadata other, Set<Key<?>> keys) { Preconditions.checkNotNull(other, "other"); // Use ByteBuffer for equals and hashCode. Map<ByteBuffer, Key<?>> asciiKeys = new HashMap<ByteBuffer, Key<?>>(keys.size()); for (Key<?> key : keys) { asciiKeys.put(ByteBuffer.wrap(key.asciiName()), key); } for (int i = 0; i < other.size; i++) { ByteBuffer wrappedNamed = ByteBuffer.wrap(other.name(i)); if (asciiKeys.containsKey(wrappedNamed)) { maybeExpand(); name(size, other.name(i)); value(size, other.value(i)); size++; } } }
/** * Returns all the metadata entries named 'name', in the order they were received, parsed as T, or * null if there are none. The iterator is not guaranteed to be "live." It may or may not be * accurate if Metadata is mutated. */ @Nullable public <T> Iterable<T> getAll(final Key<T> key) { for (int i = 0; i < size; i++) { if (bytesEqual(key.asciiName(), name(i))) { return new IterableAt<T>(key, i); } } return null; }
/** * Adds the {@code key, value} pair. If {@code key} already has values, {@code value} is added to * the end. Duplicate values for the same key are permitted. * * @throws NullPointerException if key or value is null */ public <T> void put(Key<T> key, T value) { Preconditions.checkNotNull(key, "key"); Preconditions.checkNotNull(value, "value"); maybeExpand(); name(size, key.asciiName()); value(size, key.toBytes(value)); size++; }
/** * Returns all the metadata entries named 'name', in the order they were received, parsed as T, or * null if there are none. The iterator is not guaranteed to be "live." It may or may not be * accurate if Metadata is mutated. */ @Nullable public <T> Iterable<T> getAll(final Key<T> key) { for (int i = 0; i < size; i++) { if (bytesEqual(key.asciiName(), name(i))) { return new IterableAt<T>(key, i); } } return null; }
/** Remove all values for the given key. If there were no values, {@code null} is returned. */ public <T> Iterable<T> removeAll(Key<T> key) { if (isEmpty()) { return null; } int writeIdx = 0; int readIdx = 0; List<T> ret = null; for (; readIdx < size; readIdx++) { if (bytesEqual(key.asciiName(), name(readIdx))) { ret = ret != null ? ret : new ArrayList<T>(); ret.add(key.parseBytes(value(readIdx))); continue; } name(writeIdx, name(readIdx)); value(writeIdx, value(readIdx)); writeIdx++; } int newSize = writeIdx; // Multiply by two since namesAndValues is interleaved. Arrays.fill(namesAndValues, writeIdx * 2, len(), null); size = newSize; return ret; }
/** Remove all values for the given key. If there were no values, {@code null} is returned. */ public <T> Iterable<T> removeAll(Key<T> key) { if (isEmpty()) { return null; } int writeIdx = 0; int readIdx = 0; List<T> ret = null; for (; readIdx < size; readIdx++) { if (bytesEqual(key.asciiName(), name(readIdx))) { ret = ret != null ? ret : new ArrayList<T>(); ret.add(key.parseBytes(value(readIdx))); continue; } name(writeIdx, name(readIdx)); value(writeIdx, value(readIdx)); writeIdx++; } int newSize = writeIdx; // Multiply by two since namesAndValues is interleaved. Arrays.fill(namesAndValues, writeIdx * 2, len(), null); size = newSize; return ret; }
/** * Returns the last metadata entry added with the name 'name' parsed as T. * * @return the parsed metadata entry or null if there are none. */ @Nullable public <T> T get(Key<T> key) { for (int i = size - 1; i >= 0; i--) { if (bytesEqual(key.asciiName(), name(i))) { return key.parseBytes(value(i)); } } return null; }
/** * Remove all values for the given key without returning them. This is a minor performance * optimization if you do not need the previous values. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4691") public <T> void discardAll(Key<T> key) { if (isEmpty()) { return; } int writeIdx = 0; int readIdx = 0; for (; readIdx < size; readIdx++) { if (bytesEqual(key.asciiName(), name(readIdx))) { continue; } name(writeIdx, name(readIdx)); value(writeIdx, value(readIdx)); writeIdx++; } int newSize = writeIdx; // Multiply by two since namesAndValues is interleaved. Arrays.fill(namesAndValues, writeIdx * 2, len(), null); size = newSize; }
/** * Remove all values for the given key without returning them. This is a minor performance * optimization if you do not need the previous values. */ @ExperimentalApi public <T> void discardAll(Key<T> key) { if (isEmpty()) { return; } int writeIdx = 0; int readIdx = 0; for (; readIdx < size; readIdx++) { if (bytesEqual(key.asciiName(), name(readIdx))) { continue; } name(writeIdx, name(readIdx)); value(writeIdx, value(readIdx)); writeIdx++; } int newSize = writeIdx; // Multiply by two since namesAndValues is interleaved. Arrays.fill(namesAndValues, writeIdx * 2, len(), null); size = newSize; }
/** * Returns the last metadata entry added with the name 'name' parsed as T. * * @return the parsed metadata entry or null if there are none. */ @Nullable public <T> T get(Key<T> key) { for (int i = size - 1; i >= 0; i--) { if (bytesEqual(key.asciiName(), name(i))) { return key.parseBytes(value(i)); } } return null; }
/** * Adds the {@code key, value} pair. If {@code key} already has values, {@code value} is added to * the end. Duplicate values for the same key are permitted. * * @throws NullPointerException if key or value is null */ public <T> void put(Key<T> key, T value) { Preconditions.checkNotNull(key, "key"); Preconditions.checkNotNull(value, "value"); maybeExpand(); name(size, key.asciiName()); value(size, key.toBytes(value)); size++; }