    protected void doWriteObject(final Object original, final boolean unshared) throws IOException {
        final ClassExternalizerFactory classExternalizerFactory = this.classExternalizerFactory;
        final ObjectResolver objectResolver = this.objectResolver;
        Object obj = original;
        Class<?> objClass;
        int id;
        boolean isArray, isEnum;
        SerializableClass info;
        boolean unreplaced = true;
        final int configuredVersion = this.configuredVersion;
        try {
            for (;;) {
                if (obj == null) {
                    write(ID_NULL);
                    return;
                }
                final int rid;
                if (! unshared && (rid = instanceCache.get(obj, -1)) != -1) {
                    final int diff = rid - instanceSeq;
                    if (diff >= -256) {
                        write(ID_REPEAT_OBJECT_NEAR);
                        write(diff);
                    } else if (diff >= -65536) {
                        write(ID_REPEAT_OBJECT_NEARISH);
                        writeShort(diff);
                    } else {
                        write(ID_REPEAT_OBJECT_FAR);
                        writeInt(rid);
                    }
                    return;
                }
                final ObjectTable.Writer objectTableWriter;
                if (! unshared && (objectTableWriter = objectTable.getObjectWriter(obj)) != null) {
                    write(ID_PREDEFINED_OBJECT);
                    if (configuredVersion == 1) {
                        objectTableWriter.writeObject(getBlockMarshaller(), obj);
                        writeEndBlock();
                    } else {
                        objectTableWriter.writeObject(this, obj);
                    }
                    return;
                }
                objClass = obj.getClass();
                id = getBasicClasses(configuredVersion).get(objClass, -1);
                if (id == ID_CLASS_CLASS) {
                    final Class<?> classObj = (Class<?>) obj;
                    final int cid = BASIC_CLASSES_V2.get(classObj, -1);
                    switch (cid) {
                        case -1:
                        case ID_SINGLETON_MAP_OBJECT:
                        case ID_SINGLETON_SET_OBJECT:
                        case ID_SINGLETON_LIST_OBJECT:
                        case ID_EMPTY_MAP_OBJECT:
                        case ID_EMPTY_SET_OBJECT:
                        case ID_EMPTY_LIST_OBJECT: {
                            write(ID_NEW_OBJECT);
                            writeClassClass(classObj);
                            return;
                        }
                        default: {
                            write(cid);
                            return;
                        }
                    }
                }
                isEnum = obj instanceof Enum;
                isArray = objClass.isArray();
                info = isArray || isEnum || id != -1 ? null : registry.lookup(objClass);
                if (unreplaced) {
                    if (info != null) {
                        if (info.hasWriteReplace()) {
                            obj = info.callWriteReplace(obj);
                        }
                    }
                    obj = objectResolver.writeReplace(obj);
                    if (obj != original) {
                        unreplaced = false;
                        continue;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
            if (isEnum) {
                final Enum<?> theEnum = (Enum<?>) obj;
                write(ID_NEW_OBJECT);
                writeEnumClass(theEnum.getDeclaringClass());
                writeString(theEnum.name());
                instanceCache.put(obj, instanceSeq++);
                return;
            }
            switch (id) {
                case ID_BYTE_CLASS: {
                    write(ID_BYTE_OBJECT);
                    writeByte(((Byte) obj).byteValue());
                    return;
                }
                case ID_BOOLEAN_CLASS: {
                    write(((Boolean) obj).booleanValue() ? ID_BOOLEAN_OBJECT_TRUE : ID_BOOLEAN_OBJECT_FALSE);
                    return;
                }
                case ID_CHARACTER_CLASS: {
                    write(ID_CHARACTER_OBJECT);
                    writeChar(((Character) obj).charValue());
                    return;
                }
                case ID_DOUBLE_CLASS: {
                    write(ID_DOUBLE_OBJECT);
                    writeDouble(((Double) obj).doubleValue());
                    return;
                }
                case ID_FLOAT_CLASS: {
                    write(ID_FLOAT_OBJECT);
                    writeFloat(((Float) obj).floatValue());
                    return;
                }
                case ID_INTEGER_CLASS: {
                    write(ID_INTEGER_OBJECT);
                    writeInt(((Integer) obj).intValue());
                    return;
                }
                case ID_LONG_CLASS: {
                    write(ID_LONG_OBJECT);
                    writeLong(((Long) obj).longValue());
                    return;
                }
                case ID_SHORT_CLASS: {
                    write(ID_SHORT_OBJECT);
                    writeShort(((Short) obj).shortValue());
                    return;
                }
                case ID_STRING_CLASS: {
                    final String string = (String) obj;
                    final int len = string.length();
                    if (len == 0) {
                        write(ID_STRING_EMPTY);
                        return;
                    } else if (len <= 0x100) {
                        write(ID_STRING_SMALL);
                        write(len);
                    } else if (len <= 0x10000) {
                        write(ID_STRING_MEDIUM);
                        writeShort(len);
                    } else {
                        write(ID_STRING_LARGE);
                        writeInt(len);
                    }
                    shallowFlush();
                    UTFUtils.writeUTFBytes(byteOutput, string);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                        instanceSeq++;
                    } else {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    return;
                }
                case ID_BYTE_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final byte[] bytes = (byte[]) obj;
                    final int len = bytes.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_BYTE);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_BOOLEAN_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final boolean[] booleans = (boolean[]) obj;
                    final int len = booleans.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_BOOLEAN);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CHAR_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final char[] chars = (char[]) obj;
                    final int len = chars.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_CHAR);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_SHORT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final short[] shorts = (short[]) obj;
                    final int len = shorts.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_SHORT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_INT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final int[] ints = (int[]) obj;
                    final int len = ints.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_INT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_LONG_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final long[] longs = (long[]) obj;
                    final int len = longs.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_LONG);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_FLOAT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final float[] floats = (float[]) obj;
                    final int len = floats.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_FLOAT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_DOUBLE_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final double[] doubles = (double[]) obj;
                    final int len = doubles.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_DOUBLE);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_HASH_SET:
                case ID_CC_LINKED_HASH_SET:
                case ID_CC_TREE_SET:
                case ID_CC_ARRAY_LIST:
                case ID_CC_LINKED_LIST:
                case ID_CC_VECTOR:
                case ID_CC_STACK:
                case ID_CC_ARRAY_DEQUE: {
                    instanceCache.put(obj, instanceSeq++);
                    final Collection<?> collection = (Collection<?>) obj;
                    final int len = collection.size();
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_ENUM_SET_PROXY: {
                    instanceCache.put(obj, instanceSeq++);
                    final Enum[] elements = getEnumSetElements(obj);
                    final int len = elements.length;
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_HASH_MAP:
                case ID_CC_HASHTABLE:
                case ID_CC_IDENTITY_HASH_MAP:
                case ID_CC_LINKED_HASH_MAP:
                case ID_CC_TREE_MAP:
                case ID_CC_ENUM_MAP: {
                    instanceCache.put(obj, instanceSeq++);
                    final Map<?, ?> map = (Map<?, ?>) obj;
                    final int len = map.size();
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_EMPTY_MAP_OBJECT:
                case ID_EMPTY_SET_OBJECT:
                case ID_EMPTY_LIST_OBJECT:
                case ID_REVERSE_ORDER_OBJECT: {
                    write(id);
                    return;
                }
                case ID_SINGLETON_MAP_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    final Map.Entry entry = (Map.Entry) ((Map) obj).entrySet().iterator().next();
                    doWriteObject(entry.getKey(), false);
                    doWriteObject(entry.getValue(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_SINGLETON_LIST_OBJECT:
                case ID_SINGLETON_SET_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    doWriteObject(((Collection)obj).iterator().next(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_REVERSE_ORDER2_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    try {
                        doWriteObject(Protocol.reverseOrder2Field.get(obj), false);
                    } catch (IllegalAccessException e) {
                        throw new InvalidObjectException("Cannot access standard field for reverse-order comparator");
                    }
                    return;
                }
                case ID_CC_CONCURRENT_HASH_MAP:
                case ID_CC_COPY_ON_WRITE_ARRAY_LIST:
                case ID_CC_COPY_ON_WRITE_ARRAY_SET: {
                    info = registry.lookup(objClass);
                    break;
                }
                case ID_PAIR: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    Pair<?, ?> pair = (Pair<?, ?>) obj;
                    doWriteObject(pair.getA(), unshared);
                    doWriteObject(pair.getB(), unshared);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_NCOPIES: {
                    List<?> list = (List<?>) obj;
                    int size = list.size();
                    if (size == 0) {
                        write(ID_EMPTY_LIST_OBJECT);
                        return;
                    }
                    instanceCache.put(obj, instanceSeq++);
                    if (size <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(size);
                    } else if (size <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(size);
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(size);
                    }
                    write(id);
                    doWriteObject(list.iterator().next(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case -1: break;
                default: throw new NotSerializableException(objClass.getName());
            }
            if (isArray) {
                instanceCache.put(obj, instanceSeq++);
                final Object[] objects = (Object[]) obj;
                final int len = objects.length;
                if (len == 0) {
                    write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                    writeClass(objClass.getComponentType());
                } else if (len <= 256) {
                    write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                    write(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                } else if (len <= 65536) {
                    write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                    writeShort(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                } else {
                    write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                    writeInt(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                }
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (Proxy.isProxyClass(objClass)) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                instanceCache.put(obj, instanceSeq++);
                writeProxyClass(objClass);
                doWriteObject(Proxy.getInvocationHandler(obj), false);
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            Externalizer externalizer;
            if (externalizers.containsKey(objClass)) {
                externalizer = externalizers.get(objClass);
            } else {
                externalizer = classExternalizerFactory.getExternalizer(objClass);
                externalizers.put(objClass, externalizer);
            }
            if (externalizer != null) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                writeExternalizerClass(objClass, externalizer);
                instanceCache.put(obj, instanceSeq++);
                final ObjectOutput objectOutput;
                objectOutput = getObjectOutput();
                externalizer.writeExternal(obj, objectOutput);
                writeEndBlock();
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (obj instanceof Externalizable) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                instanceCache.put(obj, instanceSeq++);
                final Externalizable ext = (Externalizable) obj;
                final ObjectOutput objectOutput = getObjectOutput();
                writeExternalizableClass(objClass);
                ext.writeExternal(objectOutput);
                writeEndBlock();
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (serializabilityChecker.isSerializable(objClass)) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                writeSerializableClass(objClass);
                instanceCache.put(obj, instanceSeq++);
                doWriteSerializableObject(info, obj, objClass);
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            throw new NotSerializableException(objClass.getName());
        } finally {
            if (! unreplaced && obj != original) {
                final int replId = instanceCache.get(obj, -1);
                if (replId != -1) {
                    instanceCache.put(original, replId);
                }
            }
        }
    }

    protected void doWriteObject(final Object original, final boolean unshared) throws IOException {
        final ClassExternalizerFactory classExternalizerFactory = this.classExternalizerFactory;
        final ObjectResolver objectResolver = this.objectResolver;
        Object obj = original;
        Class<?> objClass;
        int id;
        boolean isArray, isEnum;
        SerializableClass info;
        boolean unreplaced = true;
        final int configuredVersion = this.configuredVersion;
        try {
            for (;;) {
                if (obj == null) {
                    write(ID_NULL);
                    return;
                }
                final int rid;
                if (! unshared && (rid = instanceCache.get(obj, -1)) != -1) {
                    final int diff = rid - instanceSeq;
                    if (diff >= -256) {
                        write(ID_REPEAT_OBJECT_NEAR);
                        write(diff);
                    } else if (diff >= -65536) {
                        write(ID_REPEAT_OBJECT_NEARISH);
                        writeShort(diff);
                    } else {
                        write(ID_REPEAT_OBJECT_FAR);
                        writeInt(rid);
                    }
                    return;
                }
                final ObjectTable.Writer objectTableWriter;
                if (! unshared && (objectTableWriter = objectTable.getObjectWriter(obj)) != null) {
                    write(ID_PREDEFINED_OBJECT);
                    if (configuredVersion == 1) {
                        objectTableWriter.writeObject(getBlockMarshaller(), obj);
                        writeEndBlock();
                    } else {
                        objectTableWriter.writeObject(this, obj);
                    }
                    return;
                }
                objClass = obj.getClass();
                id = getBasicClasses(configuredVersion).get(objClass, -1);
                if (id == ID_CLASS_CLASS) {
                    final Class<?> classObj = (Class<?>) obj;
                    final int cid = BASIC_CLASSES_V2.get(classObj, -1);
                    switch (cid) {
                        case -1:
                        case ID_SINGLETON_MAP_OBJECT:
                        case ID_SINGLETON_SET_OBJECT:
                        case ID_SINGLETON_LIST_OBJECT:
                        case ID_EMPTY_MAP_OBJECT:
                        case ID_EMPTY_SET_OBJECT:
                        case ID_EMPTY_LIST_OBJECT: {
                            write(ID_NEW_OBJECT);
                            writeClassClass(classObj);
                            return;
                        }
                        default: {
                            write(cid);
                            return;
                        }
                    }
                }
                isEnum = obj instanceof Enum;
                isArray = objClass.isArray();
                info = isArray || isEnum || id != -1 ? null : registry.lookup(objClass);
                if (unreplaced) {
                    if (info != null) {
                        if (info.hasWriteReplace()) {
                            obj = info.callWriteReplace(obj);
                        }
                    }
                    obj = objectResolver.writeReplace(obj);
                    if (obj != original) {
                        unreplaced = false;
                        continue;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
            if (isEnum) {
                final Enum<?> theEnum = (Enum<?>) obj;
                write(ID_NEW_OBJECT);
                writeEnumClass(theEnum.getDeclaringClass());
                writeString(theEnum.name());
                instanceCache.put(obj, instanceSeq++);
                return;
            }
            switch (id) {
                case ID_BYTE_CLASS: {
                    write(ID_BYTE_OBJECT);
                    writeByte(((Byte) obj).byteValue());
                    return;
                }
                case ID_BOOLEAN_CLASS: {
                    write(((Boolean) obj).booleanValue() ? ID_BOOLEAN_OBJECT_TRUE : ID_BOOLEAN_OBJECT_FALSE);
                    return;
                }
                case ID_CHARACTER_CLASS: {
                    write(ID_CHARACTER_OBJECT);
                    writeChar(((Character) obj).charValue());
                    return;
                }
                case ID_DOUBLE_CLASS: {
                    write(ID_DOUBLE_OBJECT);
                    writeDouble(((Double) obj).doubleValue());
                    return;
                }
                case ID_FLOAT_CLASS: {
                    write(ID_FLOAT_OBJECT);
                    writeFloat(((Float) obj).floatValue());
                    return;
                }
                case ID_INTEGER_CLASS: {
                    write(ID_INTEGER_OBJECT);
                    writeInt(((Integer) obj).intValue());
                    return;
                }
                case ID_LONG_CLASS: {
                    write(ID_LONG_OBJECT);
                    writeLong(((Long) obj).longValue());
                    return;
                }
                case ID_SHORT_CLASS: {
                    write(ID_SHORT_OBJECT);
                    writeShort(((Short) obj).shortValue());
                    return;
                }
                case ID_STRING_CLASS: {
                    final String string = (String) obj;
                    final int len = string.length();
                    if (len == 0) {
                        write(ID_STRING_EMPTY);
                        return;
                    } else if (len <= 0x100) {
                        write(ID_STRING_SMALL);
                        write(len);
                    } else if (len <= 0x10000) {
                        write(ID_STRING_MEDIUM);
                        writeShort(len);
                    } else {
                        write(ID_STRING_LARGE);
                        writeInt(len);
                    }
                    shallowFlush();
                    UTFUtils.writeUTFBytes(byteOutput, string);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                        instanceSeq++;
                    } else {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    return;
                }
                case ID_BYTE_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final byte[] bytes = (byte[]) obj;
                    final int len = bytes.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_BYTE);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_BYTE);
                        write(bytes, 0, len);
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_BOOLEAN_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final boolean[] booleans = (boolean[]) obj;
                    final int len = booleans.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_BOOLEAN);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_BOOLEAN);
                        writeBooleanArray(booleans);
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CHAR_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final char[] chars = (char[]) obj;
                    final int len = chars.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_CHAR);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_CHAR);
                        for (int i = 0; i < len; i ++) {
                            writeChar(chars[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_SHORT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final short[] shorts = (short[]) obj;
                    final int len = shorts.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_SHORT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_SHORT);
                        for (int i = 0; i < len; i ++) {
                            writeShort(shorts[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_INT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final int[] ints = (int[]) obj;
                    final int len = ints.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_INT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_INT);
                        for (int i = 0; i < len; i ++) {
                            writeInt(ints[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_LONG_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final long[] longs = (long[]) obj;
                    final int len = longs.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_LONG);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_LONG);
                        for (int i = 0; i < len; i ++) {
                            writeLong(longs[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_FLOAT_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final float[] floats = (float[]) obj;
                    final int len = floats.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_FLOAT);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_FLOAT);
                        for (int i = 0; i < len; i ++) {
                            writeFloat(floats[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_DOUBLE_ARRAY_CLASS: {
                    if (! unshared) {
                        instanceCache.put(obj, instanceSeq++);
                    }
                    final double[] doubles = (double[]) obj;
                    final int len = doubles.length;
                    if (len == 0) {
                        write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                        write(ID_PRIM_DOUBLE);
                    } else if (len <= 256) {
                        write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                        write(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                        writeShort(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    } else {
                        write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                        writeInt(len);
                        write(ID_PRIM_DOUBLE);
                        for (int i = 0; i < len; i ++) {
                            writeDouble(doubles[i]);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_HASH_SET:
                case ID_CC_LINKED_HASH_SET:
                case ID_CC_TREE_SET:
                case ID_CC_ARRAY_LIST:
                case ID_CC_LINKED_LIST:
                case ID_CC_VECTOR:
                case ID_CC_STACK:
                case ID_CC_ARRAY_DEQUE: {
                    instanceCache.put(obj, instanceSeq++);
                    final Collection<?> collection = (Collection<?>) obj;
                    final int len = collection.size();
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        if (id == ID_CC_TREE_SET) {
                            doWriteObject(((TreeSet)collection).comparator(), false);
                        }
                        for (Object o : collection) {
                            doWriteObject(o, false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_ENUM_SET_PROXY: {
                    instanceCache.put(obj, instanceSeq++);
                    final Enum[] elements = getEnumSetElements(obj);
                    final int len = elements.length;
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        writeClass(getEnumSetElementType(obj));
                        for (Object o : elements) {
                            doWriteObject(o, false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_HASH_MAP:
                case ID_CC_HASHTABLE:
                case ID_CC_IDENTITY_HASH_MAP:
                case ID_CC_LINKED_HASH_MAP:
                case ID_CC_TREE_MAP:
                case ID_CC_ENUM_MAP: {
                    instanceCache.put(obj, instanceSeq++);
                    final Map<?, ?> map = (Map<?, ?>) obj;
                    final int len = map.size();
                    if (len == 0) {
                        write(unshared ? ID_COLLECTION_EMPTY_UNSHARED : ID_COLLECTION_EMPTY);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                    } else if (len <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    } else if (len <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(len);
                        write(id);
                        switch (id) {
                            case ID_CC_TREE_MAP: doWriteObject(((TreeMap)map).comparator(), false); break;
                            case ID_CC_ENUM_MAP: writeClass(getEnumMapKeyType(obj)); break;
                        }
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                            doWriteObject(entry.getKey(), false);
                            doWriteObject(entry.getValue(), false);
                        }
                    }
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_EMPTY_MAP_OBJECT:
                case ID_EMPTY_SET_OBJECT:
                case ID_EMPTY_LIST_OBJECT:
                case ID_REVERSE_ORDER_OBJECT: {
                    write(id);
                    return;
                }
                case ID_SINGLETON_MAP_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    final Map.Entry entry = (Map.Entry) ((Map) obj).entrySet().iterator().next();
                    doWriteObject(entry.getKey(), false);
                    doWriteObject(entry.getValue(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_SINGLETON_LIST_OBJECT:
                case ID_SINGLETON_SET_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    doWriteObject(((Collection)obj).iterator().next(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_REVERSE_ORDER2_OBJECT: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    try {
                        doWriteObject(Protocol.reverseOrder2Field.get(obj), false);
                    } catch (IllegalAccessException e) {
                        throw new InvalidObjectException("Cannot access standard field for reverse-order comparator");
                    }
                    return;
                }
                case ID_CC_CONCURRENT_HASH_MAP:
                case ID_CC_COPY_ON_WRITE_ARRAY_LIST:
                case ID_CC_COPY_ON_WRITE_ARRAY_SET: {
                    info = registry.lookup(objClass);
                    break;
                }
                case ID_PAIR: {
                    instanceCache.put(obj, instanceSeq++);
                    write(id);
                    Pair<?, ?> pair = (Pair<?, ?>) obj;
                    doWriteObject(pair.getA(), unshared);
                    doWriteObject(pair.getB(), unshared);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case ID_CC_NCOPIES: {
                    List<?> list = (List<?>) obj;
                    int size = list.size();
                    if (size == 0) {
                        write(ID_EMPTY_LIST_OBJECT);
                        return;
                    }
                    instanceCache.put(obj, instanceSeq++);
                    if (size <= 256) {
                        write(unshared ? ID_COLLECTION_SMALL_UNSHARED : ID_COLLECTION_SMALL);
                        write(size);
                    } else if (size <= 65536) {
                        write(unshared ? ID_COLLECTION_MEDIUM_UNSHARED : ID_COLLECTION_MEDIUM);
                        writeShort(size);
                    } else {
                        write(unshared ? ID_COLLECTION_LARGE_UNSHARED : ID_COLLECTION_LARGE);
                        writeInt(size);
                    }
                    write(id);
                    doWriteObject(list.iterator().next(), false);
                    if (unshared) {
                        instanceCache.put(obj, -1);
                    }
                    return;
                }
                case -1: break;
                default: throw new NotSerializableException(objClass.getName());
            }
            if (isArray) {
                instanceCache.put(obj, instanceSeq++);
                final Object[] objects = (Object[]) obj;
                final int len = objects.length;
                if (len == 0) {
                    write(unshared ? ID_ARRAY_EMPTY_UNSHARED : ID_ARRAY_EMPTY);
                    writeClass(objClass.getComponentType());
                } else if (len <= 256) {
                    write(unshared ? ID_ARRAY_SMALL_UNSHARED : ID_ARRAY_SMALL);
                    write(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                } else if (len <= 65536) {
                    write(unshared ? ID_ARRAY_MEDIUM_UNSHARED : ID_ARRAY_MEDIUM);
                    writeShort(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                } else {
                    write(unshared ? ID_ARRAY_LARGE_UNSHARED : ID_ARRAY_LARGE);
                    writeInt(len);
                    writeClass(objClass.getComponentType());
                    for (int i = 0; i < len; i++) {
                        doWriteObject(objects[i], unshared);
                    }
                }
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (Proxy.isProxyClass(objClass)) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                instanceCache.put(obj, instanceSeq++);
                writeProxyClass(objClass);
                doWriteObject(Proxy.getInvocationHandler(obj), false);
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            Externalizer externalizer;
            if (externalizers.containsKey(objClass)) {
                externalizer = externalizers.get(objClass);
            } else {
                externalizer = classExternalizerFactory.getExternalizer(objClass);
                externalizers.put(objClass, externalizer);
            }
            if (externalizer != null) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                writeExternalizerClass(objClass, externalizer);
                instanceCache.put(obj, instanceSeq++);
                final ObjectOutput objectOutput;
                objectOutput = getObjectOutput();
                externalizer.writeExternal(obj, objectOutput);
                writeEndBlock();
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (obj instanceof Externalizable) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                final Externalizable ext = (Externalizable) obj;
                final ObjectOutput objectOutput = getObjectOutput();
                writeExternalizableClass(objClass);
                instanceCache.put(obj, instanceSeq++);
                ext.writeExternal(objectOutput);
                writeEndBlock();
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            if (serializabilityChecker.isSerializable(objClass)) {
                write(unshared ? ID_NEW_OBJECT_UNSHARED : ID_NEW_OBJECT);
                writeSerializableClass(objClass);
                instanceCache.put(obj, instanceSeq++);
                doWriteSerializableObject(info, obj, objClass);
                if (unshared) {
                    instanceCache.put(obj, -1);
                }
                return;
            }
            throw new NotSerializableException(objClass.getName());
        } finally {
            if (! unreplaced && obj != original) {
                final int replId = instanceCache.get(obj, -1);
                if (replId != -1) {
                    instanceCache.put(original, replId);
                }
            }
        }
    }
