/*
 * Copyright (C) 2005-2022 Sebastiano Vigna
 *
 * 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 it.unimi.dsi.fastutil.io;

import static it.unimi.dsi.fastutil.BigArrays.SEGMENT_MASK;
import static it.unimi.dsi.fastutil.BigArrays.length;
import static it.unimi.dsi.fastutil.BigArrays.start;
import static it.unimi.dsi.fastutil.BigArrays.segment;
import static it.unimi.dsi.fastutil.BigArrays.displacement;
import static it.unimi.dsi.fastutil.BigArrays.ensureOffsetLength;
import static it.unimi.dsi.fastutil.Arrays.ensureOffsetLength;

import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;

import java.io.*;
import java.util.*;
import it.unimi.dsi.fastutil.bytes.*;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.doubles.*;
import it.unimi.dsi.fastutil.booleans.*;
import it.unimi.dsi.fastutil.chars.*;
import it.unimi.dsi.fastutil.shorts.*;
import it.unimi.dsi.fastutil.floats.*;

/** Provides static methods to perform easily binary I/O.
 *
 * <p>This class fills some gaps in the Java API. First of all, you have two
 * buffered, easy-to-use methods to {@linkplain #storeObject(Object,CharSequence) store an object to a file}
 * or {@linkplain #loadObject(CharSequence) load an object from a file},
 * and two
 * buffered, easy-to-use methods to {@linkplain #storeObject(Object,OutputStream) store an object to an output stream}
 * or to {@linkplain #loadObject(InputStream) load an object from an input stream}.
 *
 * <p>Second, a natural operation on sequences of primitive elements is to load or
 * store them in binary form using the {@link DataInput} conventions.  This
 * method is much more flexible than storing arrays as objects, as it allows
 * for partial load, partial store, and makes it easy to read the
 * resulting files from other languages.
 *
 * <p>For each primitive type, this class provides methods that read elements
 * from a {@link DataInput}, an {@link InputStream} or from a file into an array
 * or a {@linkplain it.unimi.dsi.fastutil.BigArrays big array}, or expose those elements as a type-specific {@link Iterator iterator}.
 * There are also methods that let you choose a {@linkplain ByteOrder byte order} and
 * that work with {@link ReadableByteChannel byte channels}.
 * Analogously, there are
 * methods that store the content of a (big) array (fragment) or the elements
 * returned by an iterator to a {@link DataOutput}, to an {@link OutputStream}, 
 * to a {@link WritableByteChannel}, or to a given file. Files
 * are buffered using {@link FastBufferedInputStream} and {@link FastBufferedOutputStream},
 * or, when possible, with a {@linkplain ByteBuffer byte buffer} allocated with
 * {@linkplain ByteBuffer#allocateDirect(int)}.
 *
 * <p>Since bytes can be read from or written to any stream, additional methods
 * makes it possible to {@linkplain #loadBytes(InputStream,byte[]) load bytes from} and
 * {@linkplain #storeBytes(byte[],OutputStream) store bytes to} a stream. Such methods
 * use the bulk-read methods of {@link InputStream} and {@link OutputStream}, but they
 * also include a workaround for <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6478546">bug #6478546</a>.
 *
 * <p>The store methods with a specified byte order are particularly useful when writing
 * data that is intended to be {@linkplain it.unimi.dsi.fastutil.ints.IntMappedBigList mapped into memory},
 * as using the {@linkplain ByteOrder#nativeOrder() native order} enhances performances significantly.
 *
 * @since 4.4
 */

public class BinIO {
	/** The size used with {@link ByteBuffer#allocateDirect(int)}. */
	public static int BUFFER_SIZE = 8192;

	private BinIO() {}

	/** Stores an object in a file given by a {@link File} object.
	 *
	 * @param o an object.
	 * @param file a file.
	 * @see #loadObject(File)
	 */
	public static void storeObject(final Object o, final File file) throws IOException {
		final ObjectOutputStream oos = new ObjectOutputStream(new FastBufferedOutputStream(new FileOutputStream(file)));
		oos.writeObject(o);
		oos.close();
	}

	/** Stores an object in a file given by a filename.
	 *
	 * @param o an object.
	 * @param filename a filename.
	 * @see #loadObject(CharSequence)
	 */

	public static void storeObject(final Object o, final CharSequence filename) throws IOException {
		storeObject(o, new File(filename.toString()));
	}

	/** Loads an object from a file given by a {@link File} object.
	 *
	 * @param file a file.
	 * @return the object stored under the given file.
	 * @see #storeObject(Object, File)
	 */
	public static Object loadObject(final File file) throws IOException, ClassNotFoundException {
		final ObjectInputStream ois = new ObjectInputStream(new FastBufferedInputStream(new FileInputStream(file)));
		final Object result = ois.readObject();
		ois.close();
		return result;
	}

	/** Loads an object from a file given by a filename.
	 *
	 * @param filename a filename.
	 * @return the object stored under the given filename.
	 * @see #storeObject(Object, CharSequence)
	 */
	public static Object loadObject(final CharSequence filename) throws IOException, ClassNotFoundException {
		return loadObject(new File(filename.toString()));
	}

	/** Stores an object in a given output stream.
	 *
	 * <p>This method buffers {@code s}, and flushes all wrappers after
	 * calling {@code writeObject()}, but does not close {@code s}.
	 *
	 * @param o an object.
	 * @param s an output stream.
	 * @see #loadObject(InputStream)
	 */

	public static void storeObject(final Object o, final OutputStream s) throws IOException {
		@SuppressWarnings("resource")
		final ObjectOutputStream oos = new ObjectOutputStream(new FastBufferedOutputStream(s));
		oos.writeObject(o);
		oos.flush();
	}

	/** Loads an object from a given input stream.
	 *
	 * <p><STRONG>Warning</STRONG>: this method buffers the input stream. As a consequence,
	 * subsequent reads from the same stream may not give the desired results, as bytes
	 * may have been read by the internal buffer, but not used by {@code readObject()}.
	 * This is a feature, as this method is targeted at one-shot reading from streams,
	 * e.g., reading exactly one object from {@link System#in}.
	 *
	 * @param s an input stream.
	 * @return the object read from the given input stream.
	 * @see #storeObject(Object, OutputStream)
	 */
	public static Object loadObject(final InputStream s) throws IOException, ClassNotFoundException {
		@SuppressWarnings("resource")
		final ObjectInputStream ois = new ObjectInputStream(new FastBufferedInputStream(s));
		final Object result = ois.readObject();
		return result;
	}

#include "src/it/unimi/dsi/fastutil/io/BooleanBinIOFragment.h"
#undef KEY_CLASS_Boolean
#include "src/it/unimi/dsi/fastutil/io/ByteBinIOFragment.h"
#undef KEY_CLASS_Byte
#include "src/it/unimi/dsi/fastutil/io/CharBinIOFragment.h"
#undef KEY_CLASS_Character
#include "src/it/unimi/dsi/fastutil/io/ShortBinIOFragment.h"
#undef KEY_CLASS_Short
#include "src/it/unimi/dsi/fastutil/io/IntBinIOFragment.h"
#undef KEY_CLASS_Integer
#include "src/it/unimi/dsi/fastutil/io/FloatBinIOFragment.h"
#undef KEY_CLASS_Float
#include "src/it/unimi/dsi/fastutil/io/LongBinIOFragment.h"
#undef KEY_CLASS_Long
#include "src/it/unimi/dsi/fastutil/io/DoubleBinIOFragment.h"
#undef KEY_CLASS_Double

}
