com.oracle.bmc.http.client.pki.Utf8 Maven / Gradle / Ivy
/**
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
*/
package com.oracle.bmc.http.client.pki;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
/**
* Mutable byte buffer for UTF-8 encoded text. When the text has been consumed it MUST be erased via
* {@link #close()} to avoid leaking sensitive data on the heap.
*/
interface Utf8 extends CharSequence, Sensitive {
/**
* Wrap a mutable byte array. Does not claim ownership of the array. Caller is responsible for
* erasing the byte array
*
* @param bytes The bytes to wrap
* @return Utf8 instance
*/
static Utf8 of(byte[] bytes) {
return new Chars(CharBuffer.wrap(Text.chars(bytes)));
}
/**
* Buffer the contents of a stream into a Utf8 buffer. The contents will be erased when the
* buffer is closed.
*
* @param content The content to buffer
* @return Utf8 buffer
* @throws IOException
*/
static Utf8 of(ReadableByteChannel content) throws IOException {
try (final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
final WritableByteChannel sink = Channels.newChannel(bytes)) {
final ByteBuffer buffer = ByteBuffer.allocate(4096);
while (content.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
/* write() might not write all of the bytes in a single pass */
sink.write(buffer);
}
buffer.clear();
}
buffer.flip();
while (buffer.hasRemaining()) {
sink.write(buffer);
}
final byte[] buffered = bytes.toByteArray();
try {
return Utf8.of(buffered);
} finally {
Eraser.erase(buffer);
Eraser.erase(buffered);
}
}
}
/**
* Wrap an immutable String.
*
* @param text The string to wrap
* @return Utf8 instance
*/
static Utf8 of(final String text) {
return new Wrapper(text);
}
@Override
Utf8 subSequence(int start, int end);
default boolean contains(final String text) {
return indexOf(text) != -1;
}
default int indexOf(String text) {
return indexOf(text, 0);
}
default int indexOf(String text, int offset) {
int fromIndex = offset;
int sourceCount = length();
int targetCount = text.length();
int targetOffset = 0;
int sourceOffset = 0;
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = text.charAt(targetOffset);
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (charAt(i) != first) {
while (++i <= max && charAt(i) != first) ;
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && charAt(j) == text.charAt(k); j++, k++) ;
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
default Utf8 trim() {
int len = length();
int st = 0;
while ((st < len) && (charAt(st) <= ' ')) {
st++;
}
while ((st < len) && (charAt(len - 1) <= ' ')) {
len--;
}
return ((st > 0) || (len < length())) ? subSequence(st, len) : this;
}
byte[] bytes();
/**
* Strip all whitespace characters in the text
*
* @return Text with all whitespace removed
*/
Utf8 removeWhitespace();
class Wrapper implements Utf8 {
private final String target;
private Wrapper(final String target) {
this.target = target;
}
@Override
public void close() {}
@Override
public int length() {
return target.length();
}
@Override
public char charAt(int index) {
return target.charAt(index);
}
@Override
public Utf8 subSequence(int start, int end) {
return Utf8.of(target.substring(start, end));
}
@Override
public int indexOf(String text, int offset) {
return target.indexOf(text, offset);
}
@Override
public Utf8 trim() {
return Utf8.of(target.trim());
}
@Override
public byte[] bytes() {
return Text.bytes(target);
}
@Override
public Utf8 removeWhitespace() {
return Utf8.of(target.replaceAll("\\s+", ""));
}
}
/** Byte array backed Utf8 */
class Chars implements Utf8 {
private final transient CharBuffer text;
private Chars(final CharBuffer text) {
this.text = text;
}
@Override
public void close() {
Eraser.erase(text.array());
}
@Override
public int length() {
return text.length();
}
@Override
public char charAt(int index) {
final char c = text.charAt(index);
return c;
}
@Override
public Utf8 subSequence(int beginIndex, int endIndex) {
if (beginIndex == 0 && endIndex == text.length()) {
return this;
} else {
final CharBuffer subSequence = text.subSequence(beginIndex, endIndex);
return new Chars(copyOf(subSequence));
}
}
private CharBuffer copyOf(CharBuffer existing) {
final CharBuffer copy = CharBuffer.allocate(existing.remaining());
final CharBuffer temp = existing.asReadOnlyBuffer();
copy.put(temp);
copy.flip();
return copy;
}
public byte[] bytes() {
return Text.bytes(text);
}
@Override
public Utf8 removeWhitespace() {
final CharBuffer modified = CharBuffer.allocate(text.remaining());
for (int i = 0; i < text.remaining(); ++i) {
final char c = text.charAt(i);
if (c > ' ') {
modified.put(c);
}
}
modified.flip();
return new Chars(modified);
}
@Override
public String toString() {
return text.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy