/** * if out == null => automatically create/reuse a bytebuffer * * @param out */ public void resetForReUse(OutputStream out) { if (closed) throw new RuntimeException("Can't reuse closed stream"); codec.reset(); if (out != null) { codec.setOutstream(out); } objects.clearForWrite(); }
// write identical to other version, but take field values from hashmap (because of annoying // putField/getField feature) private void writeCompatibleObjectFields( Object toWrite, Map fields, FSTClazzInfo.FSTFieldInfo[] fieldInfo) throws IOException { int booleanMask = 0; int boolcount = 0; for (int i = 0; i < fieldInfo.length; i++) { try { FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[i]; boolean isarr = subInfo.isArray(); Class subInfType = subInfo.getType(); if (subInfType != boolean.class || isarr) { if (boolcount > 0) { codec.writeFByte(booleanMask << (8 - boolcount)); boolcount = 0; booleanMask = 0; } } if (subInfo.isIntegral() && !isarr) { if (subInfType == boolean.class) { if (boolcount == 8) { codec.writeFByte(booleanMask << (8 - boolcount)); boolcount = 0; booleanMask = 0; } boolean booleanValue = ((Boolean) fields.get(subInfo.getField().getName())).booleanValue(); booleanMask = booleanMask << 1; booleanMask = (booleanMask | (booleanValue ? 1 : 0)); boolcount++; } else if (subInfType == int.class) { codec.writeFInt(((Number) fields.get(subInfo.getField().getName())).intValue()); } else if (subInfType == long.class) { codec.writeFLong(((Number) fields.get(subInfo.getField().getName())).longValue()); } else if (subInfType == byte.class) { codec.writeFByte(((Number) fields.get(subInfo.getField().getName())).byteValue()); } else if (subInfType == char.class) { codec.writeFChar((char) ((Number) fields.get(subInfo.getField().getName())).intValue()); } else if (subInfType == short.class) { codec.writeFShort(((Number) fields.get(subInfo.getField().getName())).shortValue()); } else if (subInfType == float.class) { codec.writeFFloat(((Number) fields.get(subInfo.getField().getName())).floatValue()); } else if (subInfType == double.class) { codec.writeFDouble(((Number) fields.get(subInfo.getField().getName())).doubleValue()); } } else { // object Object subObject = fields.get(subInfo.getField().getName()); writeObjectWithContext(subInfo, subObject); } } catch (Exception ex) { throw FSTUtil.rethrow(ex); } } if (boolcount > 0) { codec.writeFByte(booleanMask << (8 - boolcount)); } }
@Override public void close() throws IOException { flush(); closed = true; codec.close(); resetAndClearRefs(); conf.returnObject(objects); }
public void defaultWriteObject(Object toWrite, FSTClazzInfo serializationInfo) throws IOException { if (serializationInfo.isExternalizable()) { codec.ensureFree(writeExternalWriteAhead); ((Externalizable) toWrite).writeExternal(this); } else { FSTClazzInfo.FSTFieldInfo[] fieldInfo = serializationInfo.getFieldInfo(); writeObjectFields(toWrite, serializationInfo, fieldInfo, 0, 0); } }
/** * @param clsInfo * @param referencee * @param toWrite * @return true if header already wrote object * @throws IOException */ protected boolean writeObjectHeader( final FSTClazzInfo clsInfo, final FSTClazzInfo.FSTFieldInfo referencee, final Object toWrite) throws IOException { if (toWrite.getClass() == referencee.getType() && !clsInfo.useCompatibleMode()) { return codec.writeTag(TYPED, clsInfo, 0, toWrite); } else { final Class[] possibleClasses = referencee.getPossibleClasses(); if (possibleClasses == null) { if (!codec.writeTag(OBJECT, clsInfo, 0, toWrite)) { codec.writeClass(clsInfo); return false; } else { return true; } } else { final int length = possibleClasses.length; for (int j = 0; j < length; j++) { final Class possibleClass = possibleClasses[j]; if (possibleClass == toWrite.getClass()) { codec.writeFByte(j + 1); return false; } } if (!codec.writeTag(OBJECT, clsInfo, 0, toWrite)) { codec.writeClass(clsInfo); return false; } else { return true; } } } }
public void writeObject(Object obj, Class... possibles) throws IOException { curDepth++; if (conf.isCrossPlatform()) { writeObjectInternal(obj, null); // not supported cross platform } if (possibles != null && possibles.length > 1) { for (int i = 0; i < possibles.length; i++) { Class possible = possibles[i]; codec.registerClass(possible); } } writeObjectInternal(obj, possibles); }
/** * Creates a new FSTObjectOutput stream to write data to the specified underlying output stream. * The counter <code>written</code> is set to zero. Don't create a FSTConfiguration with each * stream, just create one global static configuration and reuse it. FSTConfiguration is * threadsafe. * * @param out the underlying output stream, to be saved for later use. */ public FSTObjectOutput(OutputStream out, FSTConfiguration conf) { this.conf = conf; codec = conf.createStreamEncoder(); codec.setOutstream(out); objects = (FSTObjectRegistry) conf.getCachedObject(FSTObjectRegistry.class); if (objects == null) { objects = new FSTObjectRegistry(conf); objects.disabled = !conf.isShareReferences(); } else { objects.clearForWrite(); } }
// incoming array is already registered protected void writeArray(FSTClazzInfo.FSTFieldInfo referencee, Object array) throws IOException { if (array == null) { codec.writeClass(Object.class); codec.writeFInt(-1); return; } final int len = Array.getLength(array); Class<?> componentType = array.getClass().getComponentType(); codec.writeClass(array.getClass()); codec.writeFInt(len); if (!componentType.isArray()) { if (codec.isPrimitiveArray(array, componentType)) { codec.writePrimitiveArray(array, 0, len); } else { // objects Object arr[] = (Object[]) array; for (int i = 0; i < len; i++) { Object toWrite = arr[i]; writeObjectWithContext(referencee, toWrite); } } } else { // multidim array. FIXME shared refs to subarrays are not tested !!! Object[] arr = (Object[]) array; FSTClazzInfo.FSTFieldInfo ref1 = new FSTClazzInfo.FSTFieldInfo( referencee.getPossibleClasses(), null, conf.getCLInfoRegistry().isIgnoreAnnotations()); for (int i = 0; i < len; i++) { Object subArr = arr[i]; boolean needsWrite = true; if (codec.isTagMultiDimSubArrays()) { if (subArr == null) { needsWrite = !codec.writeTag(NULL, null, 0, null); } else { needsWrite = !codec.writeTag(ARRAY, subArr, 0, subArr); } } if (needsWrite) writeArray(ref1, subArr); } } }
@Override public void writeFloat(float v) throws IOException { codec.writeFFloat(v); }
@Override public void write(byte[] b) throws IOException { codec.writePrimitiveArray(b, 0, b.length); }
public void ensureFree(int bytes) throws IOException { codec.ensureFree(bytes); }
@Override public void write(byte[] b, int off, int len) throws IOException { codec.writePrimitiveArray(b, off, len); }
private void writeObjectFields( Object toWrite, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo[] fieldInfo, int startIndex, int version) throws IOException { try { int booleanMask = 0; int boolcount = 0; final int length = fieldInfo.length; int j = startIndex; if (!codec.isWritingAttributes()) { for (; ; j++) { if (j == length || fieldInfo[j].getVersion() != version) { if (boolcount > 0) { codec.writeFByte(booleanMask << (8 - boolcount)); } break; } final FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[j]; if (subInfo.getIntegralType() != subInfo.BOOL) { if (boolcount > 0) { codec.writeFByte(booleanMask << (8 - boolcount)); } break; } else { if (boolcount == 8) { codec.writeFByte(booleanMask << (8 - boolcount)); boolcount = 0; booleanMask = 0; } boolean booleanValue = subInfo.getBooleanValue(toWrite); booleanMask = booleanMask << 1; booleanMask = (booleanMask | (booleanValue ? 1 : 0)); boolcount++; } } } for (int i = j; i < length; i++) { final FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[i]; if (subInfo.getVersion() != version) { codec.writeVersionTag(subInfo.getVersion()); writeObjectFields(toWrite, serializationInfo, fieldInfo, i, subInfo.getVersion()); return; } codec.writeAttributeName(subInfo); if (subInfo.isPrimitive()) { // speed safe int integralType = subInfo.getIntegralType(); switch (integralType) { case FSTClazzInfo.FSTFieldInfo.BOOL: codec.writeFByte(subInfo.getBooleanValue(toWrite) ? 1 : 0); break; case FSTClazzInfo.FSTFieldInfo.BYTE: codec.writeFByte(subInfo.getByteValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.CHAR: codec.writeFChar((char) subInfo.getCharValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.SHORT: codec.writeFShort((short) subInfo.getShortValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.INT: codec.writeFInt(subInfo.getIntValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.LONG: codec.writeFLong(subInfo.getLongValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.FLOAT: codec.writeFFloat(subInfo.getFloatValue(toWrite)); break; case FSTClazzInfo.FSTFieldInfo.DOUBLE: codec.writeFDouble(subInfo.getDoubleValue(toWrite)); break; } } else if (subInfo.isConditional()) { final int conditional = codec.getWritten(); codec.skip(4); // object Object subObject = subInfo.getObjectValue(toWrite); if (subObject == null) { codec.writeTag(NULL, null, 0, toWrite); } else { writeObjectWithContext(subInfo, subObject); } int v = codec.getWritten(); codec.writeInt32At(conditional, v); } else { // object Object subObject = subInfo.getObjectValue(toWrite); if (subObject == null) { codec.writeTag(NULL, null, 0, toWrite); } else { writeObjectWithContext(subInfo, subObject); } } } codec.writeVersionTag((byte) 0); } catch (IllegalAccessException ex) { throw FSTUtil.rethrow(ex); } }
@Override public void writeBoolean(boolean v) throws IOException { codec.writeFByte(v ? 1 : 0); }
// splitting this slows down ... protected void writeObjectWithContext(FSTClazzInfo.FSTFieldInfo referencee, Object toWrite) throws IOException { int startPosition = codec.getWritten(); boolean dontShare = objects.disabled; objectWillBeWritten(toWrite, startPosition); try { if (toWrite == null) { codec.writeTag(NULL, null, 0, toWrite); return; } final Class clazz = toWrite.getClass(); if (clazz == String.class) { String[] oneOf = referencee.getOneOf(); if (oneOf != null) { for (int i = 0; i < oneOf.length; i++) { String s = oneOf[i]; if (s.equals(toWrite)) { codec.writeTag(ONE_OF, oneOf, i, toWrite); codec.writeFByte(i); return; } } } if (dontShare) { codec.writeTag(STRING, toWrite, 0, toWrite); codec.writeStringUTF((String) toWrite); return; } } else if (clazz == Integer.class) { codec.writeTag(BIG_INT, null, 0, toWrite); codec.writeFInt(((Integer) toWrite).intValue()); return; } else if (clazz == Long.class) { codec.writeTag(BIG_LONG, null, 0, toWrite); codec.writeFLong(((Long) toWrite).longValue()); return; } else if (clazz == Boolean.class) { codec.writeTag( ((Boolean) toWrite).booleanValue() ? BIG_BOOLEAN_TRUE : BIG_BOOLEAN_FALSE, null, 0, toWrite); return; } else if ((referencee.getType() != null && referencee.getType().isEnum()) || toWrite instanceof Enum) { if (!codec.writeTag(ENUM, toWrite, 0, toWrite)) { boolean isEnumClass = toWrite.getClass().isEnum(); if (!isEnumClass) { // weird stuff .. Class c = toWrite.getClass(); while (c != null && !c.isEnum()) { c = toWrite.getClass().getEnclosingClass(); } if (c == null) { throw new RuntimeException("Can't handle this enum: " + toWrite.getClass()); } codec.writeClass(c); } else { codec.writeClass(getFstClazzInfo(referencee, toWrite.getClass())); } codec.writeFInt(((Enum) toWrite).ordinal()); } return; } FSTClazzInfo serializationInfo = getFstClazzInfo(referencee, clazz); // check for identical / equal objects FSTObjectSerializer ser = serializationInfo.getSer(); if (!dontShare && !referencee.isFlat() && !serializationInfo.isFlat() && (ser == null || !ser.alwaysCopy())) { int handle = objects.registerObjectForWrite(toWrite, codec.getWritten(), serializationInfo, tmp); // determine class header if (handle >= 0) { final boolean isIdentical = tmp[0] == 0; // objects.getReadRegisteredObject(handle) == toWrite; if (isIdentical) { // System.out.println("POK writeHandle"+handle+" // "+toWrite.getClass().getName()); if (!codec.writeTag(HANDLE, null, handle, toWrite)) codec.writeFInt(handle); return; } } } if (clazz.isArray()) { if (codec.writeTag(ARRAY, toWrite, 0, toWrite)) return; // some codecs handle primitive arrays like an primitive type writeArray(referencee, toWrite); } else if (ser == null) { // default write object wihtout custom serializer // handle write replace if (!dontShare) { if (serializationInfo.getWriteReplaceMethod() != null) { Object replaced = null; try { replaced = serializationInfo.getWriteReplaceMethod().invoke(toWrite); } catch (Exception e) { throw FSTUtil.rethrow(e); } if (replaced != toWrite) { toWrite = replaced; serializationInfo = getClassInfoRegistry().getCLInfo(toWrite.getClass()); // fixme: update object map ? } } // clazz uses some JDK special stuff (frequently slow) if (serializationInfo.useCompatibleMode() && !serializationInfo.isExternalizable()) { writeObjectCompatible(referencee, toWrite, serializationInfo); return; } } if (!writeObjectHeader( serializationInfo, referencee, toWrite)) { // skip in case codec can write object as primitive defaultWriteObject(toWrite, serializationInfo); if (serializationInfo.isExternalizable()) codec.externalEnd(serializationInfo); } } else { // object has custom serializer // Object header (nothing written till here) int pos = codec.getWritten(); if (!writeObjectHeader( serializationInfo, referencee, toWrite)) { // skip in case code can write object as primitive // write object depending on type (custom, externalizable, serializable/java, default) ser.writeObject(this, toWrite, serializationInfo, referencee, pos); codec.externalEnd(serializationInfo); } } } finally { objectHasBeenWritten(toWrite, startPosition, codec.getWritten()); } }
/** * Flushes this data output stream. This forces any buffered output bytes to be written out to the * stream. * * <p>The <code>flush</code> method of <code>DataOutputStream</code> calls the <code>flush</code> * method of its underlying output stream. * * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterOutputStream#out * @see java.io.OutputStream#flush() */ @Override public void flush() throws IOException { codec.flush(); resetAndClearRefs(); }
@Override public void writeUTF(String s) throws IOException { codec.writeStringUTF(s); }
void resetAndClearRefs() { codec.reset(); objects.clearForWrite(); }
public void resetForReUse(byte[] out) { if (closed) throw new RuntimeException("Can't reuse closed stream"); codec.reset(); codec.reset(out); objects.clearForWrite(); }
@Override public void writeByte(int v) throws IOException { codec.writeFByte(v); }
@Override public void writeLong(long v) throws IOException { codec.writeFLong(v); }
@Override public void writeInt(int v) throws IOException { codec.writeFInt(v); }
@Override public void writeChar(int v) throws IOException { codec.writeFChar((char) v); }
@Override public void writeShort(int v) throws IOException { codec.writeFShort((short) v); }
public void writeStringUTF(String str) throws IOException { codec.writeStringUTF(str); }
@Override public void writeDouble(double v) throws IOException { codec.writeFDouble(v); }
@Override public void writeBytes(String s) throws IOException { byte[] bytes = s.getBytes(); codec.writePrimitiveArray(bytes, 0, bytes.length); }
@Override public void writeChars(String s) throws IOException { char[] chars = s.toCharArray(); codec.writePrimitiveArray(chars, 0, chars.length); }
/** * serialize without an underlying stream, the resulting byte array of writing to this * FSTObjectOutput can be accessed using getBuffer(), the size using getWritten(). * * <p>Don't create a FSTConfiguration with each stream, just create one global static * configuration and reuse it. FSTConfiguration is threadsafe. * * @param conf * @throws IOException */ public FSTObjectOutput(FSTConfiguration conf) { this(null, conf); codec.setOutstream(null); }
/** * serialize without an underlying stream, the resulting byte array of writing to this * FSTObjectOutput can be accessed using getBuffer(), the size using getWritten(). Note once you * call close or flush, the tmp byte array is lost. (grab array before flushing/closing) * * <p>uses default configuration singleton * * @throws IOException */ public FSTObjectOutput() { this(null, FSTConfiguration.getDefaultConfiguration()); codec.setOutstream(null); }