/*****************************************************************************

Copyright (c) 2013, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA

*****************************************************************************/

/**************************************************//**
@file include/page0size.h
A class describing a page size.

Created Nov 14, 2013 Vasil Dimov
*******************************************************/

#ifndef page0size_t
#define page0size_t

#include "fsp0types.h"

#define FIELD_REF_SIZE 20U

/** A BLOB field reference full of zero, for use in assertions and
tests.Initially, BLOB field references are set to zero, in
dtuple_convert_big_rec(). */
extern const byte field_ref_zero[FIELD_REF_SIZE];

#define PAGE_SIZE_T_SIZE_BITS	17

/** Page size descriptor. Contains the physical and logical page size, as well
as whether the page is compressed or not. */
class page_size_t {
public:
	/** Constructor from (physical, logical, is_compressed).
	@param[in]	physical	physical (on-disk/zipped) page size
	@param[in]	logical		logical (in-memory/unzipped) page size
	@param[in]	is_compressed	whether the page is compressed */
	page_size_t(ulint physical, ulint logical, bool is_compressed)
	{
		if (physical == 0) {
			physical = UNIV_PAGE_SIZE_ORIG;
		}
		if (logical == 0) {
			logical = UNIV_PAGE_SIZE_ORIG;
		}

		m_physical = static_cast<unsigned>(physical);
		m_logical = static_cast<unsigned>(logical);
		m_is_compressed = static_cast<unsigned>(is_compressed);

		ut_ad(physical <= (1 << PAGE_SIZE_T_SIZE_BITS));
		ut_ad(logical <= (1 << PAGE_SIZE_T_SIZE_BITS));

		ut_ad(ut_is_2pow(physical));
		ut_ad(ut_is_2pow(logical));

		ut_ad(logical <= UNIV_PAGE_SIZE_MAX);
		ut_ad(logical >= physical);
		ut_ad(!is_compressed || physical <= UNIV_ZIP_SIZE_MAX);
	}

	/** Constructor from (fsp_flags).
	@param[in]	fsp_flags	filespace flags */
	explicit page_size_t(ulint fsp_flags)
	{
		ulint	ssize = FSP_FLAGS_GET_PAGE_SSIZE(fsp_flags);

		/* If the logical page size is zero in fsp_flags, then use the
		legacy 16k page size. */
		ssize = (0 == ssize) ? UNIV_PAGE_SSIZE_ORIG : ssize;

		/* Convert from a 'log2 minus 9' to a page size in bytes. */
		const unsigned	size = ((UNIV_ZIP_SIZE_MIN >> 1) << ssize);

		ut_ad(size <= UNIV_PAGE_SIZE_MAX);
		ut_ad(size <= (1 << PAGE_SIZE_T_SIZE_BITS));

		m_logical = size;

		ssize = FSP_FLAGS_GET_ZIP_SSIZE(fsp_flags);

		/* If the fsp_flags have zero in the zip_ssize field, then it means
		that the tablespace does not have compressed pages and the physical
		page size is the same as the logical page size. */
		if (ssize == 0) {
			m_is_compressed = false;
			m_physical = m_logical;
		} else {
			m_is_compressed = true;

			/* Convert from a 'log2 minus 9' to a page size
			in bytes. */
			const unsigned	phy
				= ((UNIV_ZIP_SIZE_MIN >> 1) << ssize);

			ut_ad(phy <= UNIV_ZIP_SIZE_MAX);
			ut_ad(phy <= (1 << PAGE_SIZE_T_SIZE_BITS));

			m_physical = phy;
		}
	}

	/** Retrieve the physical page size (on-disk).
	@return physical page size in bytes */
	inline ulint physical() const
	{
		ut_ad(m_physical > 0);

		return(m_physical);
	}

	/** Retrieve the logical page size (in-memory).
	@return logical page size in bytes */
	inline ulint logical() const
	{
		ut_ad(m_logical > 0);
		return(m_logical);
	}

	/** Check whether the page is compressed on disk.
	@return true if compressed */
	inline bool is_compressed() const
	{
		return(m_is_compressed);
	}

	/** Copy the values from a given page_size_t object.
	@param[in]	src	page size object whose values to fetch */
	inline void copy_from(const page_size_t& src)
	{
		*this = src;
	}

	/** Check if a given page_size_t object is equal to the current one.
	@param[in]	a	page_size_t object to compare
	@return true if equal */
	inline bool equals_to(const page_size_t& a) const
	{
		return(a.physical() == m_physical
		       && a.logical() == m_logical
		       && a.is_compressed() == m_is_compressed);
	}

private:

	/* For non compressed tablespaces, physical page size is equal to
	the logical page size and the data is stored in buf_page_t::frame
	(and is also always equal to univ_page_size (--innodb-page-size=)).

	For compressed tablespaces, physical page size is the compressed
	page size as stored on disk and in buf_page_t::zip::data. The logical
	page size is the uncompressed page size in memory - the size of
	buf_page_t::frame (currently also always equal to univ_page_size
	(--innodb-page-size=)). */

	/** Physical page size. */
	unsigned	m_physical:PAGE_SIZE_T_SIZE_BITS;

	/** Logical page size. */
	unsigned	m_logical:PAGE_SIZE_T_SIZE_BITS;

	/** Flag designating whether the physical page is compressed, which is
	true IFF the whole tablespace where the page belongs is compressed. */
	unsigned	m_is_compressed:1;
};

/* Overloading the global output operator to conveniently print an object
of type the page_size_t.
@param[in,out]	out	the output stream
@param[in]	obj	an object of type page_size_t to be printed
@retval	the output stream */
inline
std::ostream&
operator<<(
	std::ostream&		out,
	const page_size_t&	obj)
{
	out << "[page size: physical=" << obj.physical()
		<< ", logical=" << obj.logical()
		<< ", compressed=" << obj.is_compressed() << "]";
	return(out);
}

extern page_size_t	univ_page_size;

#endif /* page0size_t */
