/* * Copyright 2016-2020 chronicle.software * * https://chronicle.software * * 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 net.openhft.chronicle.bytes; import net.openhft.chronicle.core.Maths; import net.openhft.chronicle.core.io.IORuntimeException; import net.openhft.chronicle.core.io.UnsafeText; import org.jetbrains.annotations.NotNull; import java.io.Writer; import java.math.BigDecimal; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; /** * Methods to append text to a Bytes. This extends the Appendable interface. */ @SuppressWarnings({"rawtypes", "unchecked"}) public interface ByteStringAppender> extends StreamingDataOutput, Appendable { /** * @return these Bytes as a Writer */ default Writer writer() { return new ByteStringWriter(this); } /** * Append a char in UTF-8 * * @param ch to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded */ @Override default B append(char ch) throws BufferOverflowException { BytesInternal.appendUtf8Char(this, ch); return (B) this; } /** * Append a characters in UTF-8 * * @param cs to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded */ @Override default B append( CharSequence cs) throws BufferOverflowException { if (cs.length() == 0) return (B) this; try { return append(cs, 0, cs.length()); } catch (IndexOutOfBoundsException e) { throw new AssertionError(e); } } /** * Append a boolean as T or F * * @param flag to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(boolean flag) throws BufferOverflowException { return append(flag ? 'T' : 'F'); } /** * Append an int in decimal * * @param value to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(int value) throws BufferOverflowException { BytesInternal.appendBase10(this, value); return (B) this; } /** * Append a long in decimal * * @param value to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(long value) throws BufferOverflowException { if (value == (int) value) BytesInternal.appendBase10(this, (int) value); else BytesInternal.appendBase10(this, value); return (B) this; } default B appendBase(long value, int base) throws BufferOverflowException { BytesInternal.append(this, value, base); return (B) this; } default B appendBase16(long value) throws BufferOverflowException { BytesInternal.appendBase16(this, value, 1); return (B) this; } default B appendBase16(long value, int minDigits) throws BufferOverflowException { BytesInternal.appendBase16(this, value, minDigits); return (B) this; } /** * Append a long in decimal with a given number of decimal places. Print value * 10^-decimalPlaces * * @param value to append * @param decimalPlaces to shift the decimal place. * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B appendDecimal(long value, int decimalPlaces) throws BufferOverflowException { BytesInternal.appendDecimal(this, value, decimalPlaces); return (B) this; } /** * Append a float in decimal notation * * @param f to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(float f) throws BufferOverflowException { float f2 = Math.abs(f); if (f2 > 1e6 || f2 < 1e-3) { return append(Float.toString(f)); } int precision = (int) Math.floor(6 - Math.log10(f2)); long tens = Maths.tens(precision); return append((double) Math.round(f * tens) / tens); } /** * Append a double in decimal notation * * @param d to append * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(double d) throws BufferOverflowException { BytesInternal.append(this, d); return (B) this; } /** * Append a double in decimal notation to a specific number of decimal places. Trailing zeros are not truncated. *
* If the number would normally be printed with more decimal places, the number is rounded. * * @param d to append * @param decimalPlaces to always produce * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IORuntimeException if an error occurred while attempting to resize the underlying buffer */ default B append(double d, int decimalPlaces) throws BufferOverflowException { if (decimalPlaces < 0) throw new IllegalArgumentException(); if (decimalPlaces < 18) { double d2 = d * Maths.tens(decimalPlaces); if (d2 < Long.MAX_VALUE && d2 > Long.MIN_VALUE) { // changed from java.lang.Math.round(d2) as this was shown up to cause latency long round = d2 > 0.0 ? (long) (d2 + 0.5) : (long) (d2 - 0.5); if (canWriteDirect(20 + decimalPlaces)) { long address = addressForWritePosition(); long address2 = UnsafeText.appendBase10d(address, round, decimalPlaces); writeSkip(address2 - address); } else { appendDecimal(round, decimalPlaces); } return (B) this; } } return append(d); } /** * Append a portion of a String to the Bytes in UTF-8. * * @param cs to copy * @param start index of the first char inclusive * @param end index of the last char exclusive. * @return this * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded */ @Override default B append( CharSequence cs, int start, int end) throws IndexOutOfBoundsException, BufferOverflowException { BytesInternal.appendUtf8(this, cs, start, end - start); return (B) this; } /** * Append a String to the Bytes in ISO-8859-1 * * @param cs to write * @return this * @throws BufferOverflowException If the string as too large to write in the capacity available * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded */ default B append8bit( CharSequence cs) throws BufferOverflowException, BufferUnderflowException, IndexOutOfBoundsException { return append8bit(cs, 0, cs.length()); } default B append8bit( BytesStore bs) throws BufferOverflowException, BufferUnderflowException, IndexOutOfBoundsException { return write(bs, 0L, bs.readRemaining()); } default B append8bit( String cs) throws BufferOverflowException, BufferUnderflowException, IndexOutOfBoundsException { return append8bit(cs, 0, cs.length()); } /** * Append a portion of a String to the Bytes in ISO-8859-1 * * @param cs to copy * @param start index of the first char inclusive * @param end index of the last char exclusive. * @return this * @throws BufferOverflowException If the string as too large to write in the capacity available * @throws BufferUnderflowException if the capacity of the underlying buffer was exceeded * @throws IndexOutOfBoundsException if the start or the end are not valid for the CharSequence */ default B append8bit( CharSequence cs, int start, int end) throws IllegalArgumentException, BufferOverflowException, BufferUnderflowException, IndexOutOfBoundsException { if (cs instanceof BytesStore) { return write((BytesStore) cs, (long) start, end); } for (int i = start; i < end; i++) { char c = cs.charAt(i); if (c > 255) c = '?'; writeByte((byte) c); } return (B) this; } default B append8bit( BytesStore bs, long start, long end) throws IllegalArgumentException, BufferOverflowException, BufferUnderflowException, IndexOutOfBoundsException { return write(bs, start, end); } default B appendDateMillis(long dateInMillis) { BytesInternal.appendDateMillis(this, dateInMillis); return (B) this; } default B appendTimeMillis(long timeOfDayInMillis) { BytesInternal.appendTimeMillis(this, timeOfDayInMillis % 86400_000L); return (B) this; } default B append( BigDecimal bigDecimal) { append(bigDecimal.toString()); return (B) this; } }