/
area/
classes/net/sourceforge/pain/console/
classes/net/sourceforge/pain/logic/
classes/net/sourceforge/pain/logic/event/
classes/net/sourceforge/pain/logic/fn/util/
classes/net/sourceforge/pain/plugin/
classes/net/sourceforge/pain/plugin/reset/
classes/net/sourceforge/pain/plugin/shutdown/
classes/net/sourceforge/pain/plugin/social/
classes/net/sourceforge/pain/util/
classest/net/sourceforge/pain/db/data/
doc/
doc/paindb/resources/
src/net/sourceforge/pain/console/
src/net/sourceforge/pain/console/telnet/
src/net/sourceforge/pain/logic/
src/net/sourceforge/pain/logic/event/
src/net/sourceforge/pain/logic/fn/util/
src/net/sourceforge/pain/plugin/
src/net/sourceforge/pain/plugin/command/
src/net/sourceforge/pain/plugin/reset/
src/net/sourceforge/pain/plugin/shutdown/
src/net/sourceforge/pain/plugin/social/
src/net/sourceforge/pain/util/
tests/net/sourceforge/pain/db/data/
package net.sourceforge.pain.db;

import net.sourceforge.pain.util.*;

import java.lang.reflect.*;
import java.util.*;

/**
 * User: fmike  Date: 18.03.2003  Time: 14:00:19
 */
abstract class DbClassImpl implements DbClass {

	private static final int DEFAULT_VALUES_CAPACITY = 128;
	private static final float VALUES_CAPACITY_EXT_K = 1.2F;

	private final PainDB db;
	final byte[] fieldTypes;
	final String[] fieldNames;
	private final String className;

	DbObject firstInExtent;
	private DbObject lastInExtent;
	private int nObjects = 0;
	int modCount = 0; // for extent iterator

	private final boolean hasCollections; // initialize collections mainly after allobjects instantiated
	private boolean dbClosed;

	final ClassData data;
	DbClassTransContext transContext = null;

	DbClassImpl(final PainDB db, final byte[] fieldTypes, final String[] fieldNames, final String className) {
		this.db = db;
		this.fieldTypes = fieldTypes;
		this.fieldNames = fieldNames;
		this.className = className;
		hasCollections = hasCollections();
		dbClosed = false;
		data = new ClassData();
	}

	private boolean hasCollections() {
		for (int i = 0; i < fieldTypes.length; i++) {
			final int type = fieldTypes[i];
			if (type > 19) {
				return true;
			}
		}
		return false;
	}

	public final PainDB getDB() {
		checkDbState();
		return db;
	}

	final PainDB _getDB() {
		return db;
	}

	public final int getNumberOfFields() {
		return fieldNames.length;
	}

	final byte[] getFieldTypes() {
		return fieldTypes;
	}

	final String[] getFieldNames() {
		return fieldNames;
	}

	public final String getClassName() {
		return className;
	}

	public final String getFieldName(final int n) {
		return getFieldNames()[n];
	}

	public final byte getFieldType(final int n) {
		return getFieldTypes()[n];
	}

	public final Iterator extentIterator() {
		return new DbExtentIterator(this);
	}

	public final int getNumberOfObjects() {
		return nObjects;
	}


	abstract Constructor getReadConstructor();

	final void addToExtent(final DbObject obj) {
		if (lastInExtent != null) {
			lastInExtent.next = obj;
			obj.prev = lastInExtent;
			lastInExtent = obj;
		} else {
			firstInExtent = lastInExtent = obj;
		}
		nObjects++;
		modCount++;
	}


	private void removeFromExtent(final DbObject obj) {
		if (obj == firstInExtent) {
			firstInExtent = obj.next;
		}
		if (obj == lastInExtent) {
			lastInExtent = obj.prev;
		}
		if (obj.prev != null) {
			obj.prev.next = obj.next;
		}
		if (obj.next != null) {
			obj.next.prev = obj.prev;
		}
		obj.prev = null;
		obj.next = null;
		nObjects--;
		modCount++;
	}


	/**
	 *  do not touches backupData
	 */
	final void onMarkDeleted(final DbObject obj) {
		removeFromExtent(obj);
		if (hasCollections) {
			// collections has references to other objects, this references registered as inverse,
			// we should release it
			if (transContext != null && transContext.backupData != null && obj.transContext.backupDataIndex != -1) { //if not new class and not new obj
				/** if some collections was not backuped we should backup it now */
				transContext.backupData.ensureCollectionsBackup(data, obj.dataIndex, obj.transContext.backupDataIndex);
			}
			freeCollections(obj);// free all references to other objects from SCO collections fields of deleted object
		}
	}

	private void freeCollections(final DbObject obj) {
		final int dataIndex = obj.dataIndex;
		for (int i = 0; i < fieldTypes.length; i++) {
			switch (fieldTypes[i]) {
				//++deallocateDataIndex method will null all this references during owner detach
				case DbType.LINKED_LIST:
				case DbType.ARRAY_LIST:
				case DbType.INT_KEY_MAP:
				case DbType.STRING_KEY_MAP:
				case DbType.REFERENCE_SET:
//				case DbType.STRING_SET: has no refs to other objects
					final DbCollection c = ((DbCollection) ((Object[]) data.fieldsValues[i])[dataIndex]);
					if (c != null) {
						c._clear();
					}
					break;
			}
		}
	}

	/** startup method */
	final void onDbLoaded() {
		if (hasCollections) {
			int[] image;
			for (DbObject runner = firstInExtent; runner != null; runner = runner.next) {
				final int dataIndex = runner.dataIndex;
				for (int i = 0; i < fieldTypes.length; i++) {
					switch (fieldTypes[i]) {
						case DbType.LINKED_LIST:
							// changing slot value from list image to DbLinkedList instance
							image = (int[]) ((Object[]) data.fieldsValues[i])[dataIndex];
							((Object[]) data.fieldsValues[i])[dataIndex] = (image == null || image.length == 0) ? null : new DbLinkedList(runner, image, i);//lazy instantiation if null(empty)
							break;
						case DbType.ARRAY_LIST:
							// changing slot value from list image to DbArrayList instance
							image = (int[]) ((Object[]) data.fieldsValues[i])[dataIndex];
							((Object[]) data.fieldsValues[i])[dataIndex] = (image == null || image.length == 0) ? null : new DbArrayList(runner, image, i);//lazy instantiation if null(empty)
							break;
						case DbType.INT_KEY_MAP:
							// changing slot value from map image to DbIntKeyMap instance
							image = (int[]) ((Object[]) data.fieldsValues[i])[dataIndex];
							((Object[]) data.fieldsValues[i])[dataIndex] = (image == null || image.length == 0) ? null : new DbIntKeyMap(runner, image, i);//lazy instantiation if null(empty)
							break;
						case DbType.STRING_KEY_MAP:
							// changing slot value from map image to DbIntKeyMap instance
							Object[] skmImage = (Object[]) ((Object[]) data.fieldsValues[i])[dataIndex];
							((Object[]) data.fieldsValues[i])[dataIndex] = (skmImage == null || skmImage.length == 0) ? null : new DbStringKeyMap(runner, (String[]) skmImage[0], (int[]) skmImage[1], i);//lazy instantiation if null(empty)
							break;
						case DbType.REFERENCE_SET:
							// changing slot value from set image to DbReferenceSet instance
							image = (int[]) ((Object[]) data.fieldsValues[i])[dataIndex];
							((Object[]) data.fieldsValues[i])[dataIndex] = (image == null || image.length == 0) ? null : new DbReferenceSet(runner, image, i);//lazy instantiation if null(empty)
							break;
						case DbType.STRING_SET:
							// lazy instantiation
							break;

					}
				}
			}
		}
	}

	final void setDbClosed() {
		dbClosed = true;
	}

	final void checkDbState() {
		if (dbClosed) {
			throw new RuntimeException("database was closed!");
		}
	}

	public final ClassData createClassData() {
		return new ClassData();
	}

	/**
	 * here we are sure that obj and class has the same trans context
	 * @param obj
	 * @return
	 */
	final int backupObject(final DbObject obj) {
		PAssert.that(obj.dbClass == this);
		final ClassData backup = transContext.backupData;
		final int toIndex = backup.allocateDataIndex();
		return data.backupTo(obj.dataIndex, backup, toIndex);
	}

	public final int moveBackupData(final DbObject obj, final int backupDataIndex, final ClassData backupFrom, final ClassData backupTo) {
		PAssert.that(obj.dbClass == this);
		final int toIndex = backupTo.allocateDataIndex();
		backupFrom.moveBackupData(backupDataIndex, backupTo, toIndex);
		return toIndex;
	}


	/**
	 * copy all values from current classtranscontext.backupData to data, used on rollback only
	 * @param obj
	 */
	public final void restoreObject(final DbObject obj) {
		PAssert.that(obj.dbClass == this);
		data.restoreFromBackup(obj.dataIndex, transContext.backupData, obj.transContext.backupDataIndex);
	}

	public boolean isDetached() {
		return dbClosed;
	}

	final class ClassData {

		final Object[] fieldsValues;
		private final DbIntBuffer freeDataIds = new DbIntBuffer();
		private int valuesCapacity;

		public ClassData() {
			fieldsValues = new Object[getNumberOfFields()];
			valuesCapacity = 0;
			extendValuesCapacity(DEFAULT_VALUES_CAPACITY);
		}

		private void extendValuesCapacity(final int newValuesCapacity) {
			for (int i = 0; i < fieldsValues.length; i++) {
				final Object container;
				if (fieldTypes[i] == DbType.REFERENCE) { // reference presented by two values: int(indexId) and long(versionId)
					final Object[] arr;
					final Object[] oldArr = (Object[]) fieldsValues[i];
					container = arr = new Object[2];
					arr[0] = new int[newValuesCapacity];
					arr[1] = new long[newValuesCapacity];
					if (valuesCapacity != 0) {
						System.arraycopy(oldArr[0], 0, arr[0], 0, valuesCapacity);
						System.arraycopy(oldArr[1], 0, arr[1], 0, valuesCapacity);
					}
				} else {
					switch (fieldTypes[i]) {
						case DbType.BOOLEAN:
							container = new boolean[newValuesCapacity];
							break;
						case DbType.BYTE:
							container = new byte[newValuesCapacity];
							break;
						case DbType.CHAR:
							container = new char[newValuesCapacity];
							break;
						case DbType.DOUBLE:
							container = new double[newValuesCapacity];
							break;
						case DbType.FLOAT:
							container = new float[newValuesCapacity];
							break;
						case DbType.INT:
							container = new int[newValuesCapacity];
							break;
						case DbType.LONG:
							container = new long[newValuesCapacity];
							break;
						case DbType.SHORT:
							container = new short[newValuesCapacity];
							break;
						case DbType.STRING:
							container = new String[newValuesCapacity];
							break;
						case DbType.ARRAY_OF_BYTE:
						case DbType.ARRAY_OF_CHAR:
						case DbType.ARRAY_OF_INT:
						case DbType.ARRAY_OF_STRING:
						case DbType.LINKED_LIST:
						case DbType.ARRAY_LIST:
						case DbType.INT_KEY_MAP:
						case DbType.STRING_KEY_MAP:
						case DbType.REFERENCE_SET:
						case DbType.STRING_SET:
							container = new Object[newValuesCapacity];
							break;
						default:
							throw new RuntimeException("not valid type:" + fieldTypes[i]);
					}
					if (valuesCapacity != 0) {
						System.arraycopy(fieldsValues[i], 0, container, 0, valuesCapacity);
					}
				}
				fieldsValues[i] = container;
			}
			freeDataIds.ensureCapacity(newValuesCapacity);
			for (int i = newValuesCapacity - 1; --i >= valuesCapacity;) {
				freeDataIds.add(i);
			}
			valuesCapacity = newValuesCapacity;
		}

		final synchronized int allocateDataIndex() {
			if (freeDataIds.getSize() == 0) {
				extendValuesCapacity((int) (valuesCapacity * VALUES_CAPACITY_EXT_K));
			}
			return freeDataIds.removeLast();
		}

		void deallocateDataIndex(final int index) {
			freeDataIds.add(index);
			//+ free some resources, we should  free primitives-> this slot will be reused-> should set default values for new obj
			for (int i = 0; i < fieldsValues.length; i++) {
				switch (fieldTypes[i]) {
					case DbType.BOOLEAN:
						((boolean[]) fieldsValues[i])[index] = false;
						break;
					case DbType.BYTE:
						((byte[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.CHAR:
						((char[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.DOUBLE:
						((double[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.FLOAT:
						((float[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.INT:
						((int[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.LONG:
						((long[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.SHORT:
						((short[]) fieldsValues[i])[index] = 0;
						break;
					case DbType.STRING:
						((String[]) fieldsValues[i])[index] = null;
						break;
					case DbType.REFERENCE:
						((int[]) ((Object[]) fieldsValues[i])[0])[index] = -1;
						((long[]) ((Object[]) fieldsValues[i])[1])[index] = -1;
						break;
					case DbType.ARRAY_OF_BYTE:
					case DbType.ARRAY_OF_CHAR:
					case DbType.ARRAY_OF_INT:
					case DbType.ARRAY_OF_STRING:
					case DbType.LINKED_LIST:
					case DbType.ARRAY_LIST:
					case DbType.INT_KEY_MAP:
					case DbType.REFERENCE_SET:
					case DbType.STRING_KEY_MAP:
					case DbType.STRING_SET:
						((Object[]) fieldsValues[i])[index] = null;
						break;
				}
			}
		}

		/** called only for backups during flush or after rollbacks*/
		void clear() {
			PAssert.that(this != data);
			// nothing to to really before we will use cache for classData. GC will do it's work
			for (int i = 0; i < fieldsValues.length; i++) {
				fieldsValues[i] = null;
			}
		}


		public void moveBackupData(final int fromIndex, final ClassData backupTo, final int toIndex) {
			PAssert.that(this != data && backupTo != data);//remove after tests
			final Object[] toValues = backupTo.fieldsValues;
			final Object[] fromValues = this.fieldsValues;
			for (int i = 0; i < fieldsValues.length; i++) {
				switch (fieldTypes[i]) {
					case DbType.BOOLEAN:
						((boolean[]) toValues[i])[toIndex] = ((boolean[]) fromValues[i])[fromIndex];
						break;
					case DbType.BYTE:
						((byte[]) toValues[i])[toIndex] = ((byte[]) fromValues[i])[fromIndex];
						break;
					case DbType.CHAR:
						((char[]) toValues[i])[toIndex] = ((char[]) fromValues[i])[fromIndex];
						break;
					case DbType.DOUBLE:
						((double[]) toValues[i])[toIndex] = ((double[]) fromValues[i])[fromIndex];
						break;
					case DbType.FLOAT:
						((float[]) toValues[i])[toIndex] = ((float[]) fromValues[i])[fromIndex];
						break;
					case DbType.INT:
						((int[]) toValues[i])[toIndex] = ((int[]) fromValues[i])[fromIndex];
						break;
					case DbType.LONG:
						((long[]) toValues[i])[toIndex] = ((long[]) fromValues[i])[fromIndex];
						break;
					case DbType.SHORT:
						((short[]) toValues[i])[toIndex] = ((short[]) fromValues[i])[fromIndex];
						break;
					case DbType.STRING:
						((String[]) toValues[i])[toIndex] = ((String[]) fromValues[i])[fromIndex];
						break;
					case DbType.REFERENCE:
						((int[]) ((Object[]) toValues[i])[0])[toIndex] = ((int[]) ((Object[]) fromValues[i])[0])[fromIndex];
						((long[]) ((Object[]) toValues[i])[1])[toIndex] = ((long[]) ((Object[]) fromValues[i])[1])[fromIndex];
						break;
					case DbType.ARRAY_OF_BYTE:
					case DbType.ARRAY_OF_CHAR:
					case DbType.ARRAY_OF_INT:
					case DbType.ARRAY_OF_STRING:
					case DbType.LINKED_LIST:
					case DbType.ARRAY_LIST:
					case DbType.INT_KEY_MAP:
					case DbType.STRING_KEY_MAP:
					case DbType.REFERENCE_SET:
					case DbType.STRING_SET:
						((Object[]) toValues[i])[toIndex] = ((Object[]) fromValues[i])[fromIndex];
						break;

				}
			}
		}

		public int backupTo(final int fromIndex, final ClassData backupData, final int toIndex) {
			PAssert.that(this == data); // this operation used only to save data from DbClassImpl.data transaction backupData
			final Object[] toValues = backupData.fieldsValues;
			final Object[] fromValues = this.fieldsValues;
			for (int i = 0; i < fieldsValues.length; i++) {
				switch (fieldTypes[i]) {
					case DbType.BOOLEAN:
						((boolean[]) toValues[i])[toIndex] = ((boolean[]) fromValues[i])[fromIndex];
						break;
					case DbType.BYTE:
						((byte[]) toValues[i])[toIndex] = ((byte[]) fromValues[i])[fromIndex];
						break;
					case DbType.CHAR:
						((char[]) toValues[i])[toIndex] = ((char[]) fromValues[i])[fromIndex];
						break;
					case DbType.DOUBLE:
						((double[]) toValues[i])[toIndex] = ((double[]) fromValues[i])[fromIndex];
						break;
					case DbType.FLOAT:
						((float[]) toValues[i])[toIndex] = ((float[]) fromValues[i])[fromIndex];
						break;
					case DbType.INT:
						((int[]) toValues[i])[toIndex] = ((int[]) fromValues[i])[fromIndex];
						break;
					case DbType.LONG:
						((long[]) toValues[i])[toIndex] = ((long[]) fromValues[i])[fromIndex];
						break;
					case DbType.SHORT:
						((short[]) toValues[i])[toIndex] = ((short[]) fromValues[i])[fromIndex];
						break;
					case DbType.STRING:
						((String[]) toValues[i])[toIndex] = ((String[]) fromValues[i])[fromIndex];
						break;
					case DbType.REFERENCE:
						((int[]) ((Object[]) toValues[i])[0])[toIndex] = ((int[]) ((Object[]) fromValues[i])[0])[fromIndex];
						((long[]) ((Object[]) toValues[i])[1])[toIndex] = ((long[]) ((Object[]) fromValues[i])[1])[fromIndex];
						break;
						//collections use lazy backup (backupCollection method)
						// arrays use lazy backup to
				}
			}
			return toIndex;
		}

		public void restoreFromBackup(final int toIndex, final ClassData backupData, final int backupIndex) {
			PAssert.that(this == data); // only data could be restored from backups during rollbacks
			final Object[] toValues = fieldsValues;
			final Object[] fromValues = backupData.fieldsValues;
			for (int i = 0; i < fieldsValues.length; i++) {
				switch (fieldTypes[i]) {
					case DbType.BOOLEAN:
						((boolean[]) toValues[i])[toIndex] = ((boolean[]) fromValues[i])[backupIndex];
						break;
					case DbType.BYTE:
						((byte[]) toValues[i])[toIndex] = ((byte[]) fromValues[i])[backupIndex];
						break;
					case DbType.CHAR:
						((char[]) toValues[i])[toIndex] = ((char[]) fromValues[i])[backupIndex];
						break;
					case DbType.DOUBLE:
						((double[]) toValues[i])[toIndex] = ((double[]) fromValues[i])[backupIndex];
						break;
					case DbType.FLOAT:
						((float[]) toValues[i])[toIndex] = ((float[]) fromValues[i])[backupIndex];
						break;
					case DbType.INT:
						((int[]) toValues[i])[toIndex] = ((int[]) fromValues[i])[backupIndex];
						break;
					case DbType.LONG:
						((long[]) toValues[i])[toIndex] = ((long[]) fromValues[i])[backupIndex];
						break;
					case DbType.SHORT:
						((short[]) toValues[i])[toIndex] = ((short[]) fromValues[i])[backupIndex];
						break;
					case DbType.STRING:
						((String[]) toValues[i])[toIndex] = ((String[]) fromValues[i])[backupIndex];
						break;
					case DbType.REFERENCE:
						((int[]) ((Object[]) toValues[i])[0])[toIndex] = ((int[]) ((Object[]) fromValues[i])[0])[backupIndex];
						((long[]) ((Object[]) toValues[i])[1])[toIndex] = ((long[]) ((Object[]) fromValues[i])[1])[backupIndex];
						break;
					case DbType.ARRAY_OF_BYTE:
					case DbType.ARRAY_OF_CHAR:
					case DbType.ARRAY_OF_INT:
					case DbType.ARRAY_OF_STRING:
						final Object backupArr = ((Object[]) fromValues[i])[backupIndex];
						if (backupArr != null) { // null means that array was not modified
							if (backupArr == DbConstants.ZERO_INT_ARRAY) {    // zero_int_array is used to backup null values
								((Object[]) toValues[i])[toIndex] = null;
							} else {
								((Object[]) toValues[i])[toIndex] = backupArr;
							}
						}
						break;
					case DbType.LINKED_LIST:
					case DbType.ARRAY_LIST:
					case DbType.INT_KEY_MAP:
					case DbType.STRING_KEY_MAP:
					case DbType.REFERENCE_SET:
						final Object backup = ((Object[]) fromValues[i])[backupIndex];
						if (backup != null) {//backup == null means there was no backup (no collection change)
							final DbCollection c = (DbCollection) ((Object[]) toValues[i])[toIndex];
							c.restoreFromBackup(backup);
						}
						break;
					case DbType.STRING_SET:
						final Object cbackup = ((Object[]) fromValues[i])[backupIndex];
						final Object dest = ((Object[]) toValues[i])[toIndex];
						if (dest instanceof DbStringSet) {
							((DbStringSet) dest).restoreFromBackup(cbackup);
						} else {
							((Object[]) toValues[i])[toIndex] = cbackup; //was not instantiated (backuped if obj deleted)
						}


				}
			}

		}

		/**collections use lazy backup (backup on change)
		 * @param collection
		 */
		void backupCollection(final DbCollection collection) {
			PAssert.that(this == data);

			final int toIndex = collection.owner.transContext.backupDataIndex; // backup index
			final int fid = collection.fid;
			if (toIndex == -1) {
				return; // new object
			}
			final Object[] toValues = transContext.backupData.fieldsValues;
			if (((Object[]) toValues[fid])[toIndex] != null) {
				// backup already done
				return;
			}
//			final Object[] fromValues = this.fieldsValues;
//			final int fromIndex = collection.owner.dataIndex;
//			final DbCollection c = (DbCollection) ((Object[]) fromValues[fid])[fromIndex];
			((Object[]) toValues[fid])[toIndex] = collection.createBackupImage();
		}

		/**arrays use lazy backup (backup on change)
		 */
		void backupArray(final DbObject obj, final int fid) {
			PAssert.that(this == data);
			final int toIndex = obj.transContext.backupDataIndex;
			if (toIndex == -1) {// new object
				return;
			}
			final int fromIndex = obj.dataIndex;
			final Object[] toValues = transContext.backupData.fieldsValues;

			if (((Object[]) toValues[fid])[toIndex] != null) {
				return; // already backed up
			}
			final Object[] fromValues = this.fieldsValues;
			final Object data = ((Object[]) fromValues[fid])[fromIndex];
			final Object backupData = data == null ? DbConstants.ZERO_INT_ARRAY : data;
			((Object[]) toValues[fid])[toIndex] = backupData;
		}


		public void ensureCollectionsBackup(ClassData fromData, int fromIndex, int toIndex) {
			for (int i = 0; i < fieldTypes.length; i++) {
				switch (fieldTypes[i]) {
					//++deallocateDataIndex method will null all this references during owner detach
					case DbType.LINKED_LIST:
					case DbType.ARRAY_LIST:
					case DbType.INT_KEY_MAP:
					case DbType.STRING_KEY_MAP:
					case DbType.REFERENCE_SET:
						if (((Object[]) fieldsValues[i])[toIndex] == null) { // if was not backuped
							final DbCollection fromCollection = ((DbCollection) ((Object[]) fromData.fieldsValues[i])[fromIndex]);
							if (fromCollection != null) { // null => zero capacity and was not instantiated
								((Object[]) fieldsValues[i])[toIndex] = fromCollection.createBackupImage();
							}
						}
						break;
					case DbType.STRING_SET:
						if (((Object[]) fieldsValues[i])[toIndex] == null) { // if was not backuped
							final Object o = ((Object[]) fromData.fieldsValues[i])[fromIndex];
							if (o != null && o instanceof DbStringSet) {
								((Object[]) fieldsValues[i])[toIndex] = ((DbStringSet) o).createBackupImage();
							} else { //was not instantiated (String[] or null if zero capacity)
								((Object[]) fieldsValues[i])[toIndex] = o;
							}
						}
						break;
				}
			}
		}
	};
}