content: /*
   * Copyright (C) 2020 ActiveJ LLC.
   *
   * 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 io.activej.serializer;
  
  import org.jetbrains.annotations.Nullable;
  
  import java.util.concurrent.atomic.AtomicReference;
  
  import static java.nio.charset.StandardCharsets.ISO_8859_1;
  import static java.nio.charset.StandardCharsets.UTF_8;
  
  /**
   * Provides methods for reading primitives
   * and Strings from byte arrays
   */
  @SuppressWarnings({"unused", "WeakerAccess", "DuplicatedCode", "SpellCheckingInspection"})
  public final class BinaryInput {
  	public final byte[] array;
  	public int pos;
  
  	public static final AtomicReference<char[]> BUF = new AtomicReference<>(new char[256]);
  
  	public BinaryInput(byte[] array) {
  		this.array = array;
  	}
  
  	public BinaryInput(byte[] array, int pos) {
  		this.array = array;
  		this.pos = pos;
  	}
  
  	public byte[] array() {
  		return array;
  	}
  
  	public int pos() {
  		return pos;
  	}
  
  	public void pos(int pos) {
  		this.pos = pos;
  	}
  
  	public void move(int delta) {
  		this.pos += delta;
  	}
  
  	public int read(byte[] b) {
  		return read(b, 0, b.length);
  	}
  
  	public int read(byte[] b, int off, int len) {
  		System.arraycopy(this.array, pos, b, off, len);
  		pos += len;
  		return len;
  	}
  
  	public byte readByte() {
  		return array[pos++];
  	}
  
  	public boolean readBoolean() {
  		return readByte() != 0;
  	}
  
  	public short readShort() {
  		short result = (short) ((array[pos] & 0xFF) << 8 | array[pos + 1] & 0xFF);
  		pos += 2;
  		return result;
  	}
  
  	public short readShortLE() {
  		short result = (short) (array[pos] & 0xFF | (array[pos + 1] & 0xFF) << 8);
  		pos += 2;
  		return result;
  	}
  
  	public char readChar() {
  		char c = (char) ((array[pos] & 0xFF) << 8 | array[pos + 1] & 0xFF);
  		pos += 2;
  		return c;
  	}
  
  	public char readCharLE() {
  		char c = (char) (array[pos] & 0xFF | (array[pos + 1] & 0xFF) << 8);
  		pos += 2;
  		return c;
  	}
  
  	public int readInt() {
  		int result =
  			(array[pos] & 0xFF) << 24 |
  			(array[pos + 1] & 0xFF) << 16 |
  			(array[pos + 2] & 0xFF) << 8 |
  			array[pos + 3] & 0xFF;
  		pos += 4;
  		return result;
  	}
  
  	public int readIntLE() {
  		int result =
  			(array[pos] & 0xFF) |
  			(array[pos + 1] & 0xFF) << 8 |
  			(array[pos + 2] & 0xFF) << 16 |
  			(array[pos + 3] & 0xFF) << 24;
  		pos += 4;
  		return result;
  	}
  
  	public long readLong() {
  		long result =
  			(long) (array[pos] & 0xFF) << 56 |
  			(long) (array[pos + 1] & 0xFF) << 48 |
  			(long) (array[pos + 2] & 0xFF) << 40 |
  			(long) (array[pos + 3] & 0xFF) << 32 |
  			(long) (array[pos + 4] & 0xFF) << 24 |
  			(long) (array[pos + 5] & 0xFF) << 16 |
  			(long) (array[pos + 6] & 0xFF) << 8 |
  			(long) (array[pos + 7] & 0xFF);
  		pos += 8;
  		return result;
  	}
  
  	public long readLongLE() {
  		long result =
  			(long) (array[pos] & 0xFF) |
  			(long) (array[pos + 1] & 0xFF) << 8 |
  			(long) (array[pos + 2] & 0xFF) << 16 |
  			(long) (array[pos + 3] & 0xFF) << 24 |
  			(long) (array[pos + 4] & 0xFF) << 32 |
  			(long) (array[pos + 5] & 0xFF) << 40 |
  			(long) (array[pos + 6] & 0xFF) << 48 |
  			(long) (array[pos + 7] & 0xFF) << 56;
  		pos += 8;
  		return result;
  	}
  
  	public int readVarInt() {
  		byte b;
  		if ((b = array[pos]) >= 0) {
  			pos += 1;
  			return b;
  		}
  		int result = b & 0x7f;
  		if ((b = array[pos + 1]) >= 0) {
  			pos += 2;
  			return result | b << 7;
  		}
  		result |= (b & 0x7f) << 7;
  		if ((b = array[pos + 2]) >= 0) {
  			pos += 3;
  			return result | b << 14;
  		}
  		result |= (b & 0x7f) << 14;
  		if ((b = array[pos + 3]) >= 0) {
  			pos += 4;
  			return result | b << 21;
  		}
  		result = result | (b & 0x7f) << 21 | array[pos + 4] << 28;
  		pos += 5;
  		return result;
  	}
  
  	public long readVarLong() {
  		byte b;
  		b = array[pos++];
  		if (b >= 0) {
  			return b;
  		}
  		long result = b & 0x7F;
  		for (int offset = 7; offset < 64; offset += 7) {
  			b = array[pos++];
  			if (b >= 0)
  				return result | (long) b << offset;
  			result |= (long) (b & 0x7F) << offset;
  		}
  		throw new CorruptedDataException("Read varlong was too long");
  	}
  
  	public float readFloat() {
  		return Float.intBitsToFloat(readInt());
  	}
  
  	public double readDouble() {
  		return Double.longBitsToDouble(readLong());
  	}
  
  	public String readUTF8() {
  		int length = readVarInt();
  		String s = new String(array, pos, length, UTF_8);
  		pos += length;
  		return s;
  	}
  
  	public @Nullable String readUTF8Nullable() {
  		int length = readVarInt();
  		if (length == 0) return null;
  		length--;
  		String s = new String(array, pos, length, UTF_8);
  		pos += length;
  		return s;
  	}
  
  	public String readIso88591() {
  		int length = readVarInt();
  		String s = new String(array, pos, length, ISO_8859_1);
  		pos += length;
  		return s;
  	}
  
  	public @Nullable String readIso88591Nullable() {
  		int length = readVarInt();
  		if (length == 0) return null;
  		length--;
  		String s = new String(array, pos, length, ISO_8859_1);
  		pos += length;
  		return s;
  	}
  
  	public String readUTF16() {
  		int length = readVarInt();
  		if (length == 0) return "";
  		if (length >= 40) return readUTF16buf(length);
  		char[] chars = new char[length];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) ((array[pos + i * 2] & 0xFF) << 8 | array[pos + i * 2 + 1] & 0xFF);
  		}
  		pos += length * 2;
  		return new String(chars, 0, length);
  	}
  
  	public String readUTF16LE() {
  		int length = readVarInt();
  		if (length == 0) return "";
  		if (length >= 40) return readUTF16LEbuf(length);
  		char[] chars = new char[length];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) (array[pos + i * 2] & 0xFF | (array[pos + i * 2 + 1] & 0xFF) << 8);
  		}
  		pos += length * 2;
  		return new String(chars, 0, length);
  	}
  
  	public @Nullable String readUTF16Nullable() {
  		int length = readVarInt();
  		if (length == 0) return null;
  		length--;
  		if (length == 0) return "";
  		if (length >= 40) return readUTF16buf(length);
  		char[] chars = new char[length];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) ((array[pos + i * 2] & 0xFF) << 8 | array[pos + i * 2 + 1] & 0xFF);
  		}
  		pos += length * 2;
  		return new String(chars, 0, length);
  	}
  
  	public @Nullable String readUTF16NullableLE() {
  		int length = readVarInt();
  		if (length == 0) return null;
  		length--;
  		if (length == 0) return "";
  		if (length >= 40) return readUTF16LEbuf(length);
  		char[] chars = new char[length];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) (array[pos + i * 2] & 0xFF | (array[pos + i * 2 + 1] & 0xFF) << 8);
  		}
  		pos += length * 2;
  		return new String(chars, 0, length);
  	}
  
  	private String readUTF16buf(int length) {
  		char[] chars = BUF.getAndSet(null);
  		if (chars == null || chars.length < length) chars = new char[length + length / 4];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) ((array[pos + i * 2] & 0xFF) << 8 | array[pos + i * 2 + 1] & 0xFF);
  		}
  		pos += length * 2;
  		String s = new String(chars, 0, length);
  		BUF.lazySet(chars);
  		return s;
  	}
  
  	private String readUTF16LEbuf(int length) {
  		char[] chars = BUF.getAndSet(null);
  		if (chars == null || chars.length < length) chars = new char[length + length / 4];
  		for (int i = 0; i < length; i++) {
  			chars[i] = (char) (array[pos + i * 2] & 0xFF | (array[pos + i * 2 + 1] & 0xFF) << 8);
  		}
  		pos += length * 2;
  		String s = new String(chars, 0, length);
  		BUF.lazySet(chars);
  		return s;
  	}
  }
  