final Object unswizzle(ArrayPos obj, Class cls, Object parent,
			boolean recursiveLoading) throws Exception {
		byte[] body = obj.body;
		int offs = obj.offs;
		int oid = Bytes.unpack4(body, offs);
		offs += 4;
		Object val;

		if (oid < 0) {
			switch (-1 - oid) {
			case ClassDescriptor.tpBoolean:
				val = body[offs++] != 0;
				break;
			case ClassDescriptor.tpByte:
				val = body[offs++];
				break;
			case ClassDescriptor.tpChar:
				val = (char) Bytes.unpack2(body, offs);
				offs += 2;
				break;
			case ClassDescriptor.tpShort:
				val = Bytes.unpack2(body, offs);
				offs += 2;
				break;
			case ClassDescriptor.tpInt:
				val = Bytes.unpack4(body, offs);
				offs += 4;
				break;
			case ClassDescriptor.tpLong:
				val = Bytes.unpack8(body, offs);
				offs += 8;
				break;
			case ClassDescriptor.tpFloat:
				val = Bytes.unpackF4(body, offs);
				offs += 4;
				break;
			case ClassDescriptor.tpDouble:
				val = Bytes.unpackF8(body, offs);
				offs += 8;
				break;
			case ClassDescriptor.tpDate:
				val = new Date(Bytes.unpack8(body, offs));
				offs += 8;
				break;
			case ClassDescriptor.tpString:
				obj.offs = offs;
				return Bytes.unpackString(obj, encoding);
			case ClassDescriptor.tpClass:
				obj.offs = offs;
				return ClassDescriptor.loadClass(this,
						Bytes.unpackString(obj, encoding));
			case ClassDescriptor.tpLink: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				Object[] arr = new Object[len];

				for (int j = 0; j < len; j++) {
					int elemOid = Bytes.unpack4(body, offs);
					offs += 4;

					if (elemOid != 0) {
						arr[j] = new PersistentStub(this, elemOid);
					}
				}

				val = new LinkImpl(this, arr, parent);
				break;
			}
			case ClassDescriptor.tpArrayOfByte: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				byte[] arr = new byte[len];
				System.arraycopy(body, offs, arr, 0, len);
				offs += len;
				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfBoolean: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				boolean[] arr = new boolean[len];

				for (int j = 0; j < len; j++) {
					arr[j] = body[offs++] != 0;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfShort: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				short[] arr = new short[len];

				for (int j = 0; j < len; j++) {
					arr[j] = Bytes.unpack2(body, offs);
					offs += 2;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfChar: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				char[] arr = new char[len];

				for (int j = 0; j < len; j++) {
					arr[j] = (char) Bytes.unpack2(body, offs);
					offs += 2;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfInt: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				int[] arr = new int[len];

				for (int j = 0; j < len; j++) {
					arr[j] = Bytes.unpack4(body, offs);
					offs += 4;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfLong: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				long[] arr = new long[len];

				for (int j = 0; j < len; j++) {
					arr[j] = Bytes.unpack8(body, offs);
					offs += 8;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfFloat: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				float[] arr = new float[len];

				for (int j = 0; j < len; j++) {
					arr[j] = Bytes.unpackF4(body, offs);
					offs += 4;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfDouble: {
				int len = Bytes.unpack4(body, offs);
				offs += 4;
				double[] arr = new double[len];

				for (int j = 0; j < len; j++) {
					arr[j] = Bytes.unpackF8(body, offs);
					offs += 8;
				}

				val = arr;
				break;
			}
			case ClassDescriptor.tpArrayOfObject: {
				int len = Bytes.unpack4(body, offs);
				obj.offs = offs + 4;
				Object[] arr = new Object[len];

				for (int j = 0; j < len; j++) {
					arr[j] = unswizzle(obj, Object.class, parent,
							recursiveLoading);
				}

				return arr;
			}
			case ClassDescriptor.tpArrayOfRaw: {
				int len = Bytes.unpack4(body, offs);
				int typeOid = Bytes.unpack4(body, offs + 4);
				obj.offs = offs + 8;
				Class elemType;

				if (typeOid == -1) {
					elemType = Comparable.class;
				}
				else {
					ClassDescriptor desc = findClassDescriptor(typeOid);
					elemType = desc.cls;
				}
				
				Object arr = Array.newInstance(elemType, len);

				for (int j = 0; j < len; j++) {
					Array.set(arr, j,
							unswizzle(obj, elemType, parent, recursiveLoading));
				}

				return arr;
			}
			case ClassDescriptor.tpCustom: {
				ByteArrayObjectInputStream in = new ByteArrayObjectInputStream(
						body, offs, parent, recursiveLoading, false);
				val = serializer.unpack(in);
				offs = in.getPosition();
				break;
			}
			default:
				if (oid < -ClassDescriptor.tpValueTypeBias) {
					int typeOid = -ClassDescriptor.tpValueTypeBias - oid;
					ClassDescriptor desc = findClassDescriptor(typeOid);
					val = desc.newInstance();

					if (desc.isCollection) {
						int len = Bytes.unpack4(body, offs);
						obj.offs = offs + 4;
						Collection collection = (Collection) val;

						for (int i = 0; i < len; i++) {
							collection.add(unswizzle(obj, Object.class, parent,
									recursiveLoading));
						}

						return collection;
					}
					else if (desc.isMap) {
						int len = Bytes.unpack4(body, offs);
						obj.offs = offs + 4;
						Map map = (Map) val;

						for (int i = 0; i < len; i++) {
							Object key = unswizzle(obj, Object.class, parent,
									recursiveLoading);
							Object value = unswizzle(obj, Object.class, parent,
									recursiveLoading);
							map.put(key, value);
						}

						return map;
					}
					else {
						offs = unpackObject(val, desc, recursiveLoading, body,
								offs, parent);
					}
				}
				else {
					throw new StorageError(StorageError.UNSUPPORTED_TYPE);
				}
			}
		}
		else {
			val = unswizzle(oid, cls, recursiveLoading);
		}

		obj.offs = offs;
		return val;
	}