/*
 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/* IOData.h created by rsulack on Wed 17-Sep-1997 */
/* IOData.h converted to C++ by gvdl on Fri 1998-10-30 */

#ifndef _OS_OSDATA_H
#define _OS_OSDATA_H

#include <libkern/c++/OSObject.h>
#include <libkern/c++/OSPtr.h>
#include <os/base.h>


class OSData;
class OSString;

typedef OSData* OSDataPtr;
typedef OSData const* OSDataConstPtr;

/*!
 * @header
 *
 * @abstract
 * This header declares the OSData container class.
 */


/*!
 * @class OSData
 *
 * @abstract
 * OSData wraps an array of bytes in a C++ object
 * for use in Libkern collections.
 *
 * @discussion
 * OSData represents an array of bytes as a Libkern C++ object.
 * OSData objects are mutable:
 * You can add bytes to them and
 * overwrite portions of the byte array.
 *
 * <b>Use Restrictions</b>
 *
 * With very few exceptions in the I/O Kit, all Libkern-based C++
 * classes, functions, and macros are <b>unsafe</b>
 * to use in a primary interrupt context.
 * Consult the I/O Kit documentation related to primary interrupts
 * for more information.
 *
 * OSData provides no concurrency protection;
 * it's up to the usage context to provide any protection necessary.
 * Some portions of the I/O Kit, such as
 * @link //apple_ref/doc/class/IORegistryEntry IORegistryEntry@/link,
 * handle synchronization via defined member functions for setting
 * properties.
 */
class OSData : public OSObject
{
	friend class OSSerialize;

	OSDeclareDefaultStructors(OSData);

#if APPLE_KEXT_ALIGN_CONTAINERS

protected:
	unsigned int   length;
	unsigned int   capacity;
	unsigned int   capacityIncrement;
	void         * OS_PTRAUTH_SIGNED_PTR("OSData.data") data;

#else /* APPLE_KEXT_ALIGN_CONTAINERS */

protected:
	void         * OS_PTRAUTH_SIGNED_PTR("OSData.data") data;
	unsigned int   length;
	unsigned int   capacity;
	unsigned int   capacityIncrement;

#endif /* APPLE_KEXT_ALIGN_CONTAINERS */

private:
	typedef void (*DeallocFunction)(void * ptr, unsigned int length);
protected:
	struct ExpansionData;

/* Reserved for future use. (Internal use only)  */
	ExpansionData * reserved;

public:

/*!
 * @function withCapacity
 *
 * @abstract
 * Creates and initializes an empty instance of OSData.
 *
 * @param capacity  The initial capacity of the OSData object in bytes.
 *
 * @result
 * An instance of OSData with a reference count of 1;
 * <code>NULL</code> on failure.
 *
 * @discussion
 * <code>capacity</code> may be zero.
 * The OSData object will allocate a buffer internally
 * when necessary, and will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	static OSPtr<OSData> withCapacity(unsigned int capacity __xnu_data_size);


/*!
 * @function withBytes
 *
 * @abstract
 * Creates and initializes an instance of OSData
 * with a copy of the provided data buffer.
 *
 * @param bytes     The buffer of data to copy.
 * @param numBytes  The length of <code>bytes</code>.
 *
 * @result
 * An instance of OSData containing a copy of the provided byte array,
 * with a reference count of 1;
 * <code>NULL</code> on failure.
 *
 * @discussion
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	static OSPtr<OSData> withBytes(
		const void   * bytes,
		unsigned int   numBytes __xnu_data_size);




/*!
 * @function withBytesNoCopy
 *
 * @abstract
 * Creates and initializes an instance of OSData
 * that shares the provided data buffer.
 *
 * @param bytes     The buffer of data to represent.
 * @param numBytes  The length of <code>bytes</code>.
 *
 * @result
 * A instance of OSData that shares the provided byte array,
 * with a reference count of 1;
 * <code>NULL</code> on failure.
 *
 * @discussion
 * An OSData object created with this function
 * does not claim ownership
 * of the data buffer, but shares it with the caller.
 * When the caller determines that the OSData object has actually been freed,
 * it can safely dispose of the data buffer.
 * Conversely, if it frees the shared data buffer,
 * it must not attempt to use the OSData object and should release it.
 *
 * An OSData object created with shared external data cannot append bytes,
 * but you can get the byte pointer and
 * modify bytes within the shared buffer.
 */
	static OSPtr<OSData> withBytesNoCopy(
		void         * bytes,
		unsigned int   numBytes);




/*!
 * @function withData
 *
 * @abstract
 * Creates and initializes an instance of OSData
 * with contents copied from another OSData object.
 *
 * @param inData An OSData object that provides the initial data.
 *
 * @result
 * An instance of OSData containing a copy of the data in <code>inData</code>,
 * with a reference count of 1;
 * <code>NULL</code> on failure.
 *
 * @discussion
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	static OSPtr<OSData> withData(const OSData * inData);


/*!
 * @function withData
 *
 * @abstract
 * Creates and initializes an instance of OSData
 * with contents copied from a range within another OSData object.
 *
 * @param inData    An OSData object that provides the initial data.
 * @param start     The starting index from which bytes will be copied.
 * @param numBytes  The number of bytes to be copied from <code>start</code>.
 *
 * @result
 * An instance of OSData containing a copy
 * of the specified data range from <code>inData</code>,
 * with a reference count of 1;
 * <code>NULL</code> on failure.
 *
 * @discussion
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	static OSPtr<OSData> withData(
		const OSData * inData,
		unsigned int   start,
		unsigned int   numBytes);


/*!
 * @function initWithCapacity
 *
 * @abstract
 * Initializes an instance of OSData.
 *
 * @param capacity The initial capacity of the OSData object in bytes.
 *
 * @result
 * <code>true</code> on success, <code>false</code> on failure.
 *
 * @discussion
 * Not for general use. Use the static instance creation method
 * <code>@link
 * //apple_ref/cpp/clm/OSData/withCapacity/staticOSData*\/(unsignedint)
 * withCapacity@/link</code> instead.
 *
 * <code>capacity</code> may be zero.
 * The OSData object will allocate a buffer internally
 * when necessary, and will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	virtual bool initWithCapacity(unsigned int capacity __xnu_data_size);


/*!
 * @function initWithBytes
 *
 * @abstract
 * Initializes an instance of OSData
 * with a copy of the provided data buffer.
 *
 * @param bytes     The buffer of data to copy.
 * @param numBytes  The length of <code>bytes</code>.
 *
 * @result
 * <code>true</code> on success, <code>false</code> on failure.
 *
 * @discussion
 * Not for general use. Use the static instance creation method
 * <code>@link withBytes withBytes@/link</code> instead.
 *
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	virtual bool initWithBytes(
		const void   * bytes,
		unsigned int   numBytes __xnu_data_size);




/*!
 * @function initWithBytesNoCopy
 *
 * @abstract
 * Initializes an instance of OSData
 * to share the provided data buffer.
 *
 * @param bytes     The buffer of data to represent.
 * @param numBytes  The length of <code>bytes</code>.
 *
 * @result
 * <code>true</code> on success, <code>false</code> on failure.
 *
 * @discussion
 * Not for general use. Use the static instance creation method
 * <code>@link withBytesNoCopy withBytesNoCopy@/link</code> instead.
 *
 * An OSData object initialized with this function
 * does not claim ownership
 * of the data buffer, but merely shares it with the caller.
 *
 * An OSData object created with shared external data cannot append bytes,
 * but you can get the byte pointer and
 * modify bytes within the shared buffer.
 */
	virtual bool initWithBytesNoCopy(
		void         * bytes,
		unsigned int   numBytes);




/*!
 * @function initWithData
 *
 * @abstract
 * Creates and initializes an instance of OSData
 * with contents copied from another OSData object.
 *
 * @param inData An OSData object that provides the initial data.
 *
 * @result
 * <code>true</code> on success, <code>false</code> on failure.
 *
 * @discussion
 * Not for general use. Use the static instance creation method
 * <code>@link
 * //apple_ref/cpp/clm/OSData/withData/staticOSData*\/(constOSData*)
 * withData(OSData *)@/link</code>
 * instead.
 *
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	virtual bool initWithData(const OSData * inData);


/*!
 * @function initWithData
 *
 * @abstract
 * Initializes an instance of OSData
 * with contents copied from a range within another OSData object.
 *
 * @param inData    An OSData object that provides the initial data.
 * @param start     The starting index from which bytes will be copied.
 * @param numBytes  The number of bytes to be copied from <code>start</code>.
 *
 * @result
 * Returns <code>true</code> on success, <code>false</code> on failure.
 *
 * @discussion
 * Not for general use. Use the static instance creation method
 * <code>@link
 * //apple_ref/cpp/clm/OSData/withData/staticOSData*\/(constOSData*,unsignedint,unsignedint)
 * withData(OSData *, unsigned int, unsigned int)@/link</code>
 * instead.
 *
 * The new OSData object will grow as needed to accommodate more bytes
 * (<i>unlike</i> @link //apple_ref/doc/uid/20001498 CFMutableData@/link,
 * for which a nonzero initial capacity is a hard limit).
 */
	virtual bool initWithData(
		const OSData * inData,
		unsigned int   start,
		unsigned int   numBytes);


/*!
 * @function free
 *
 * @abstract
 * Deallocates or releases any resources
 * used by the OSData instance.
 *
 * @discussion
 * This function should not be called directly;
 * use
 * <code>@link
 * //apple_ref/cpp/instm/OSObject/release/virtualvoid/()
 * release@/link</code>
 * instead.
 */
	virtual void free() APPLE_KEXT_OVERRIDE;


/*!
 * @function getLength
 *
 * @abstract
 * Returns the number of bytes in or referenced by the OSData object.
 *
 * @result
 * The number of bytes in or referenced by the OSData object.
 */
	virtual unsigned int getLength() const;


/*!
 * @function getCapacity
 *
 * @abstract
 * Returns the total number of bytes the OSData can store without reallocating.
 *
 * @result
 * The total number bytes the OSData can store without reallocating.
 *
 * @discussion
 * OSData objects grow when full to accommodate additional bytes.
 * See
 * <code>@link
 * //apple_ref/cpp/instm/OSData/getCapacityIncrement/virtualunsignedint/()
 * getCapacityIncrement@/link</code>
 * and
 * <code>@link
 * //apple_ref/cpp/instm/OSData/ensureCapacity/virtualunsignedint/(unsignedint)
 * ensureCapacity@/link</code>.
 *
 * OSData objects created or initialized to use a shared buffer
 * do not make use of this attribute, and return -1 from this function.
 */
	virtual unsigned int getCapacity() const;


/*!
 * @function getCapacityIncrement
 *
 * @abstract
 * Returns the storage increment of the OSData object.
 *
 * @result
 * The storage increment of the OSData object.
 *
 * @discussion
 * An OSData object allocates storage for bytes in multiples
 * of the capacity increment.
 *
 * OSData objects created or initialized to use a shared buffer
 * do not make use of this attribute.
 */
	virtual unsigned int getCapacityIncrement() const;


/*!
 * @function setCapacityIncrement
 *
 * @abstract
 * Sets the storage increment of the array.
 *
 * @result
 * The original storage increment of the array.
 *
 * @discussion
 * An OSArray allocates storage for objects in multiples
 * of the capacity increment.
 *
 * OSData objects created or initialized to use a shared buffer
 * do not make use of this attribute.
 */
	virtual unsigned int setCapacityIncrement(unsigned increment);


// xx-review: does not check for capacity == EXTERNAL

/*!
 * @function ensureCapacity
 *
 * @abstract
 * Ensures the array has enough space
 * to store the requested number of bytes.
 *
 * @param newCapacity The total number of bytes the OSData object
 *                    should be able to store.
 *
 * @result
 * Returns the new capacity of the OSData object,
 * which may be different from the number requested
 * (if smaller, reallocation of storage failed).
 *
 * @discussion
 * This function immediately resizes the OSData's buffer, if necessary,
 * to accommodate at least <code>newCapacity</code> bytes.
 * If <code>newCapacity</code> is not greater than the current capacity,
 * or if an allocation error occurs, the original capacity is returned.
 *
 * There is no way to reduce the capacity of an OSData.
 *
 * An OSData object created "NoCopy" does not allow resizing.
 */
	virtual unsigned int ensureCapacity(unsigned int newCapacity);


/*!
 * @function appendBytes
 *
 * @abstract
 * Appends a buffer of bytes to the OSData object's internal data buffer.
 *
 * @param bytes     A pointer to the data to append.
 *                  If <code>bytes</code> is <code>NULL</code>
 *                  then a zero-filled buffer of length <code>numBytes</code>
 *                  is appended.
 * @param numBytes  The number of bytes from <code>bytes</code> to append.
 *
 * @result
 * <code>true</code> if the new data was successfully added,
 * <code>false</code> on failure.
 *
 * @discussion
 * This function immediately resizes the OSData's buffer, if necessary,
 * to accommodate the new total size.
 *
 * An OSData object created "NoCopy" does not allow bytes
 * to be appended.
 */
	virtual bool appendBytes(
		const void   * bytes,
		unsigned int   numBytes __xnu_data_size);




/*!
 * @function appendBytes
 *
 * @abstract
 * Appends the data contained in another OSData object.
 *
 * @param aDataObj  The OSData object whose contents will be appended.
 *
 * @result
 * <code>true</code> if the new data was successfully added,
 * <code>false</code> on failure.
 *
 * @discussion
 * This function immediately resizes the OSData's buffer, if necessary,
 * to accommodate the new total size.
 *
 * An OSData object created "NoCopy" does not allow bytes
 * to be appended.
 */
	virtual bool appendBytes(const OSData * aDataObj);


/*!
 * @function getBytesNoCopy
 *
 * @abstract
 * Returns a pointer to the OSData object's internal data buffer.
 *
 * @result
 * A pointer to the OSData object's internal data buffer.
 *
 * @discussion
 * You can modify the existing contents of an OSData object
 * via this function.
 * It works with OSData objects that have their own data buffers
 * as well as with OSData objects that have shared buffers.
 *
 * If you append bytes or characters to an OSData object,
 * it may have to reallocate its internal storage,
 * rendering invalid an extrated pointer to that storage.
 */
	__xnu_returns_data_pointer
	virtual const void * getBytesNoCopy() const;


/*!
 * @function getBytesNoCopy
 *
 * @abstract
 * Returns a pointer into the OSData object's internal data buffer
 * with a given offset and length.
 *
 * @param start    The offset from the base of the internal data buffer.
 * @param numBytes The length of the  window.
 *
 * @result
 * A pointer to the bytes in the specified range
 * within the OSData object,
 * or 0 if that range does not lie completely
 * within the object's buffer.
 *
 * @discussion
 * You can modify the existing contents of an OSData object
 * via this function.
 * It works with OSData objects that have their own data buffers
 * as well as with OSData objects that have shared buffers.
 *
 * If you append bytes or characters to an OSData object,
 * it may have to reallocate its internal storage,
 * rendering invalid an extrated pointer to that storage.
 */
	__xnu_returns_data_pointer
	virtual const void * getBytesNoCopy(
		unsigned int start,
		unsigned int numBytes) const;


/*!
 * @function isEqualTo
 *
 * @abstract
 * Tests the equality of two OSData objects.
 *
 * @param aDataObj The OSData object being compared against the receiver.
 *
 * @result
 * <code>true</code> if the two OSData objects are equivalent,
 * <code>false</code> otherwise.
 *
 * @discussion
 * Two OSData objects are considered equal
 * if they have same length and if their
 * byte buffers hold the same contents.
 */
	virtual bool isEqualTo(const OSData * aDataObj) const;


/*!
 * @function isEqualTo
 *
 * @abstract
 * Tests the equality of an OSData object's contents
 * to a C array of bytes.
 *
 * @param bytes    A pointer to the bytes to compare.
 * @param numBytes The number of bytes to compare.
 *
 * @result
 * <code>true</code> if the data buffers are equal
 * over the given length,
 * <code>false</code> otherwise.
 */
	virtual bool isEqualTo(
		const void   * bytes,
		unsigned int   numBytes) const;


/*!
 * @function isEqualTo
 *
 * @abstract
 * Tests the equality of an OSData object to an arbitrary object.
 *
 * @param anObject The object to be compared against the receiver.
 *
 * @result
 * <code>true</code> if the two objects are equivalent,
 * <code>false</code> otherwise.
 *
 * @discussion
 * An OSData is considered equal to another object
 * if that object is derived from OSData
 * and contains the equivalent bytes of the same length.
 */
	virtual bool isEqualTo(const OSMetaClassBase * anObject) const APPLE_KEXT_OVERRIDE;


/*!
 * @function isEqualTo
 *
 * @abstract
 * Tests the equality of an OSData object to an OSString.
 *
 * @param aString  The string object to be compared against the receiver.
 *
 * @result
 * <code>true</code> if the two objects are equivalent,
 * <code>false</code> otherwise.
 *
 * @discussion
 * This function compares the bytes of the OSData object
 * against those of the OSString,
 * accounting for the possibility that an OSData
 * might explicitly include a nul
 * character as part of its total length.
 * Thus, for example, an OSData object containing
 * either the bytes <'u', 's', 'b', '\0'>
 * or  <'u', 's', 'b'>
 * will compare as equal to the OSString containing "usb".
 */
	virtual bool isEqualTo(const OSString * aString) const;


/*!
 * @function serialize
 *
 * @abstract
 * Archives the receiver into the provided
 * @link //apple_ref/doc/class/IORegistryEntry OSSerialize@/link object.
 *
 * @param serializer The OSSerialize object.
 *
 * @result
 * <code>true</code> if serialization succeeds, <code>false</code> if not.
 */
	virtual bool serialize(OSSerialize * serializer) const APPLE_KEXT_OVERRIDE;


/*!
 * @function appendByte
 *
 * @abstract
 * Appends a single byte value
 * to the OSData object's internal data buffer
 * a specified number of times.
 *
 * @param byte     The byte value to append.
 * @param numBytes The number of copies of <code>byte</code> to append.
 *
 * @result
 * <code>true</code> if the new data was successfully added,
 * <code>false</code> if not.
 *
 * @discussion
 * This function immediately resizes the OSData's buffer, if necessary,
 * to accommodate the new total size.
 *
 * An OSData object created "NoCopy" does not allow bytes
 * to be appended.
 */
	virtual bool appendByte(
		unsigned char byte,
		unsigned int  numBytes);


	void setSerializable(bool serializable);

private:
	virtual void setDeallocFunction(DeallocFunction func);
	bool isSerializable(void);

private:
	enum ValueAcquisition {
		kValueCopy,
		kValueNoCopy
	};


private:
	OSMetaClassDeclareReservedUsedX86(OSData, 0);
	OSMetaClassDeclareReservedUnused(OSData, 1);
	OSMetaClassDeclareReservedUnused(OSData, 2);
	OSMetaClassDeclareReservedUnused(OSData, 3);
	OSMetaClassDeclareReservedUnused(OSData, 4);
	OSMetaClassDeclareReservedUnused(OSData, 5);
	OSMetaClassDeclareReservedUnused(OSData, 6);
	OSMetaClassDeclareReservedUnused(OSData, 7);
};

#endif /* !_OS_OSDATA_H */
