<commit_before>/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * 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 com.badlogic.gdx.utils;

import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/** Builder style API for emitting UBJSON.
 * @author <NAME> */
public class UBJsonWriter implements Closeable {

	final DataOutputStream out;

	private JsonObject current;
	private boolean named;
	private final Array<JsonObject> stack = new Array();

	public UBJsonWriter (OutputStream out) {
		if (!(out instanceof DataOutputStream)) out = new DataOutputStream(out);
		this.out = (DataOutputStream)out;
	}

	/** Begins a new object container. To finish the object call {@link #pop()}.
	 * @return This writer, for chaining */
	public UBJsonWriter object () throws IOException {
		if (current != null) {
			if (!current.array) {
				if (!named) throw new IllegalStateException("Name must be set.");
				named = false;
			}
		}
		stack.add(current = new JsonObject(false));
		return this;
	}

	/** Begins a new named object container, having the given name. To finish the object call {@link #pop()}.
	 * @return This writer, for chaining */
	public UBJsonWriter object (String name) throws IOException {
		name(name).object();
		return this;
	}

	/** Begins a new array container. To finish the array call {@link #pop()}.
	 * @return this writer, for chaining. */
	public UBJsonWriter array () throws IOException {
		if (current != null) {
			if (!current.array) {
				if (!named) throw new IllegalStateException("Name must be set.");
				named = false;
			}
		}
		stack.add(current = new JsonObject(true));
		return this;
	}

	/** Begins a new named array container, having the given name. To finish the array call {@link #pop()}.
	 * @return this writer, for chaining. */
	public UBJsonWriter array (String name) throws IOException {
		name(name).array();
		return this;
	}

	/** Appends a name for the next object, array, or value.
	 * @return this writer, for chaining */
	public UBJsonWriter name (String name) throws IOException {
		if (current == null || current.array) throw new IllegalStateException("Current item must be an object.");
		byte[] bytes = name.getBytes("UTF-8");
		out.writeByte('S');
		if (bytes.length <= Byte.MAX_VALUE) {
			out.writeByte('i');
			out.writeByte(bytes.length);
		} else if (bytes.length <= Short.MAX_VALUE) {
			out.writeByte('I');
			out.writeShort(bytes.length);
		} else if (bytes.length <= Integer.MAX_VALUE) {
			out.writeByte('l');
			out.writeInt(bytes.length);
		} else if (bytes.length <= Long.MAX_VALUE) {
			out.writeByte('L');
			out.writeLong(bytes.length);
		}
		out.write(bytes);
		named = true;
		return this;
	}

	/** Appends a {@code byte} value to the stream. This corresponds to the {@code int8} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (byte value) throws IOException {
		checkName();
		out.writeByte('i');
		out.writeByte(value);
		return this;
	}

	/** Appends a {@code short} value to the stream. This corresponds to the {@code int16} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (short value) throws IOException {
		checkName();
		out.writeByte('I');
		out.writeShort(value);
		return this;
	}

	/** Appends an {@code int} value to the stream. This corresponds to the {@code int32} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (int value) throws IOException {
		checkName();
		out.writeByte('l');
		out.writeInt(value);
		return this;
	}

	/** Appends a {@code long} value to the stream. This corresponds to the {@code int64} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (long value) throws IOException {
		checkName();
		out.writeByte('L');
		out.writeLong(value);
		return this;
	}

	/** Appends a {@code float} value to the stream. This corresponds to the {@code float32} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (float value) throws IOException {
		checkName();
		out.writeByte('d');
		out.writeFloat(value);
		return this;
	}

	/** Appends a {@code double} value to the stream. This corresponds to the {@code float64} value type in the UBJSON
	 * specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (double value) throws IOException {
		checkName();
		out.writeByte('D');
		out.writeDouble(value);
		return this;
	}

	/** Appends a {@code boolean} value to the stream. This corresponds to the {@code boolean} value type in the UBJSON
	 * specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (boolean value) throws IOException {
		checkName();
		out.writeByte(value ? 'T' : 'F');
		return this;
	}

	/** Appends a {@code char} value to the stream. Because, in Java, a {@code char} is 16 bytes, this corresponds to the
	 * {@code int16} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (char value) throws IOException {
		checkName();
		out.writeByte('I');
		out.writeChar(value);
		return this;
	}

	/** Appends a {@code String} value to the stream. This corresponds to the {@code string} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (String value) throws IOException {
		checkName();
		byte[] bytes = value.getBytes("UTF-8");
		out.writeByte('S');
		if (bytes.length <= Byte.MAX_VALUE) {
			out.writeByte('i');
			out.writeByte(bytes.length);
		} else if (bytes.length <= Short.MAX_VALUE) {
			out.writeByte('I');
			out.writeShort(bytes.length);
		} else if (bytes.length <= Integer.MAX_VALUE) {
			out.writeByte('l');
			out.writeInt(bytes.length);
		} else if (bytes.length <= Long.MAX_VALUE) {
			out.writeByte('L');
			out.writeLong(bytes.length);
		}
		out.write(bytes);
		return this;
	}

	/** Appends an optimized {@code byte array} value to the stream. As an optimized array, the {@code int8} value type marker and
	 * element count are encoded once at the array marker instead of repeating the type marker for each element.
	 * @return this writer, for chaining */
	public UBJsonWriter value (byte[] values) throws IOException {
		array();
		out.writeByte('$');
		out.writeByte('i');
		out.writeByte('#');
		value(values.length);
		for (int i = 0, n = values.length; i < n; i++) {
			out.writeByte(values[i]);
		}
		pop();
		return this;
	}

	/** Appends an optimized {@code short array} value to the stream. As an optimized array, the {@code int16} value type marker and
	 * element count are encoded once at the array marker instead of repeating the type marker for each element.<commit_msg>Array length can't be more than Integer.MAX_VALUE<commit_after>/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * 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 com.badlogic.gdx.utils;

import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/** Builder style API for emitting UBJSON.
 * @author <NAME> */
public class UBJsonWriter implements Closeable {

	final DataOutputStream out;

	private JsonObject current;
	private boolean named;
	private final Array<JsonObject> stack = new Array();

	public UBJsonWriter (OutputStream out) {
		if (!(out instanceof DataOutputStream)) out = new DataOutputStream(out);
		this.out = (DataOutputStream)out;
	}

	/** Begins a new object container. To finish the object call {@link #pop()}.
	 * @return This writer, for chaining */
	public UBJsonWriter object () throws IOException {
		if (current != null) {
			if (!current.array) {
				if (!named) throw new IllegalStateException("Name must be set.");
				named = false;
			}
		}
		stack.add(current = new JsonObject(false));
		return this;
	}

	/** Begins a new named object container, having the given name. To finish the object call {@link #pop()}.
	 * @return This writer, for chaining */
	public UBJsonWriter object (String name) throws IOException {
		name(name).object();
		return this;
	}

	/** Begins a new array container. To finish the array call {@link #pop()}.
	 * @return this writer, for chaining. */
	public UBJsonWriter array () throws IOException {
		if (current != null) {
			if (!current.array) {
				if (!named) throw new IllegalStateException("Name must be set.");
				named = false;
			}
		}
		stack.add(current = new JsonObject(true));
		return this;
	}

	/** Begins a new named array container, having the given name. To finish the array call {@link #pop()}.
	 * @return this writer, for chaining. */
	public UBJsonWriter array (String name) throws IOException {
		name(name).array();
		return this;
	}

	/** Appends a name for the next object, array, or value.
	 * @return this writer, for chaining */
	public UBJsonWriter name (String name) throws IOException {
		if (current == null || current.array) throw new IllegalStateException("Current item must be an object.");
		byte[] bytes = name.getBytes("UTF-8");
		out.writeByte('S');
		if (bytes.length <= Byte.MAX_VALUE) {
			out.writeByte('i');
			out.writeByte(bytes.length);
		} else if (bytes.length <= Short.MAX_VALUE) {
			out.writeByte('I');
			out.writeShort(bytes.length);
		} else {
			out.writeByte('l');
			out.writeInt(bytes.length);
		}
		out.write(bytes);
		named = true;
		return this;
	}

	/** Appends a {@code byte} value to the stream. This corresponds to the {@code int8} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (byte value) throws IOException {
		checkName();
		out.writeByte('i');
		out.writeByte(value);
		return this;
	}

	/** Appends a {@code short} value to the stream. This corresponds to the {@code int16} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (short value) throws IOException {
		checkName();
		out.writeByte('I');
		out.writeShort(value);
		return this;
	}

	/** Appends an {@code int} value to the stream. This corresponds to the {@code int32} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (int value) throws IOException {
		checkName();
		out.writeByte('l');
		out.writeInt(value);
		return this;
	}

	/** Appends a {@code long} value to the stream. This corresponds to the {@code int64} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (long value) throws IOException {
		checkName();
		out.writeByte('L');
		out.writeLong(value);
		return this;
	}

	/** Appends a {@code float} value to the stream. This corresponds to the {@code float32} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (float value) throws IOException {
		checkName();
		out.writeByte('d');
		out.writeFloat(value);
		return this;
	}

	/** Appends a {@code double} value to the stream. This corresponds to the {@code float64} value type in the UBJSON
	 * specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (double value) throws IOException {
		checkName();
		out.writeByte('D');
		out.writeDouble(value);
		return this;
	}

	/** Appends a {@code boolean} value to the stream. This corresponds to the {@code boolean} value type in the UBJSON
	 * specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (boolean value) throws IOException {
		checkName();
		out.writeByte(value ? 'T' : 'F');
		return this;
	}

	/** Appends a {@code char} value to the stream. Because, in Java, a {@code char} is 16 bytes, this corresponds to the
	 * {@code int16} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (char value) throws IOException {
		checkName();
		out.writeByte('I');
		out.writeChar(value);
		return this;
	}

	/** Appends a {@code String} value to the stream. This corresponds to the {@code string} value type in the UBJSON specification.
	 * @return this writer, for chaining */
	public UBJsonWriter value (String value) throws IOException {
		checkName();
		byte[] bytes = value.getBytes("UTF-8");
		out.writeByte('S');
		if (bytes.length <= Byte.MAX_VALUE) {
			out.writeByte('i');
			out.writeByte(bytes.length);
		} else if (bytes.length <= Short.MAX_VALUE) {
			out.writeByte('I');
			out.writeShort(bytes.length);
		} else {
			out.writeByte('l');
			out.writeInt(bytes.length);
		}
		out.write(bytes);
		return this;
	}

	/** Appends an optimized {@code byte array} value to the stream. As an optimized array, the {@code int8} value type marker and
	 * element count are encoded once at the array marker instead of repeating the type marker for each element.
	 * @return this writer, for chaining */
	public UBJsonWriter value (byte[] values) throws IOException {
		array();
		out.writeByte('$');
		out.writeByte('i');
		out.writeByte('#');
		value(values.length);
		for (int i = 0, n = values.length; i < n; i++) {
			out.writeByte(values[i]);
		}
		pop();
		return this;
	}

	/** Appends an optimized {@code short array} value to the stream. As an optimized array, the {@code int16} value type marker and
	 * element count are encoded once at the array marker instead of repeating the type marker for each element.
{'fraction_non_alphanumeric': 0.13616041186831052, 'fraction_numerical': 0.003929006909632841, 'mean_word_length': 3.9440723375753515, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5318167', 'n_tokens_mistral': 4622, 'n_tokens_neox': 4210, 'n_words': 1639}