diff --git a/pom.xml b/pom.xml index a74a5bff..9ec607d6 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 de.ruedigermoeller fst - 2.19 + 2.20 a fast java serialization drop in-replacement + some serialization based utils (Structs, OffHeap Memory) https://code.google.com/p/fast-serialization/ @@ -120,7 +120,14 @@ 2.1 - + + + org.bouncycastle + bcprov-jdk15on + 1.51 + test + + com.cedarsoftware java-util @@ -134,11 +141,6 @@ 4.8.1 test - - org.objenesis - objenesis - 2.1 - diff --git a/src/main/java/org/nustaq/serialization/FSTDecoder.java b/src/main/java/org/nustaq/serialization/FSTDecoder.java index e2e63dfc..5214427c 100644 --- a/src/main/java/org/nustaq/serialization/FSTDecoder.java +++ b/src/main/java/org/nustaq/serialization/FSTDecoder.java @@ -73,4 +73,5 @@ public interface FSTDecoder { boolean isEndMarker(String s); int readVersionTag() throws IOException; + void pushBack(int bytes); } diff --git a/src/main/java/org/nustaq/serialization/FSTObjectInput.java b/src/main/java/org/nustaq/serialization/FSTObjectInput.java index 0edba8e4..e400a99c 100644 --- a/src/main/java/org/nustaq/serialization/FSTObjectInput.java +++ b/src/main/java/org/nustaq/serialization/FSTObjectInput.java @@ -18,6 +18,7 @@ import org.nustaq.serialization.util.FSTUtil; import java.io.*; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -920,6 +921,25 @@ ObjectInputStream getObjectInputStream(final Class cl, final FSTClazzInfo clInfo @Override public Object readObjectOverride() throws IOException, ClassNotFoundException { try { + byte b = FSTObjectInput.this.readByte(); + if ( b != FSTObjectOutput.SPECIAL_COMPATIBILITY_OBJECT_TAG ) { + Constructor[] constructors = OptionalDataException.class.getDeclaredConstructors(); + FSTObjectInput.this.pushBack(1); + for (int i = 0; i < constructors.length; i++) { + Constructor constructor = constructors[i]; + if (constructor.getParameterCount() == 1 && constructor.getParameterTypes()[0] == int.class) { + constructor.setAccessible(true); + OptionalDataException ode; + try { + ode = (OptionalDataException) constructor.newInstance(0); + throw ode; + } catch (InvocationTargetException e) { + break; + } + } + } + throw new EOFException("if your code relies on this, think"); + } return FSTObjectInput.this.readObjectInternal(referencee.getPossibleClasses()); } catch (IllegalAccessException e) { throw new IOException(e); @@ -1198,6 +1218,10 @@ public boolean markSupported() { return fakeWrapper; } + protected void pushBack(int i) { + getCodec().pushBack(i); + } + static class MyObjectStream extends ObjectInputStream { ObjectInputStream wrapped; diff --git a/src/main/java/org/nustaq/serialization/FSTObjectOutput.java b/src/main/java/org/nustaq/serialization/FSTObjectOutput.java index 386558f7..679ebfb1 100644 --- a/src/main/java/org/nustaq/serialization/FSTObjectOutput.java +++ b/src/main/java/org/nustaq/serialization/FSTObjectOutput.java @@ -34,6 +34,7 @@ */ public class FSTObjectOutput implements ObjectOutput { + public static final byte SPECIAL_COMPATIBILITY_OBJECT_TAG = -19; // see issue 52 public static final byte ONE_OF = -18; public static final byte BIG_BOOLEAN_FALSE = -17; public static final byte BIG_BOOLEAN_TRUE = -16; @@ -855,6 +856,7 @@ public void useProtocolVersion(int version) throws IOException { @Override protected void writeObjectOverride(Object obj) throws IOException { + getCodec().writeFByte( SPECIAL_COMPATIBILITY_OBJECT_TAG ); FSTObjectOutput.this.writeObjectInternal(obj, null, referencee.getPossibleClasses()); } diff --git a/src/main/java/org/nustaq/serialization/coders/FSTBytezDecoder.java b/src/main/java/org/nustaq/serialization/coders/FSTBytezDecoder.java index 858873c7..3e1c9bfc 100644 --- a/src/main/java/org/nustaq/serialization/coders/FSTBytezDecoder.java +++ b/src/main/java/org/nustaq/serialization/coders/FSTBytezDecoder.java @@ -428,4 +428,9 @@ public int readVersionTag() throws IOException { return readFByte(); } + @Override + public void pushBack(int bytes) { + pos -= bytes; + } + } diff --git a/src/main/java/org/nustaq/serialization/coders/FSTMinBinDecoder.java b/src/main/java/org/nustaq/serialization/coders/FSTMinBinDecoder.java index 0fdee136..5ae54791 100644 --- a/src/main/java/org/nustaq/serialization/coders/FSTMinBinDecoder.java +++ b/src/main/java/org/nustaq/serialization/coders/FSTMinBinDecoder.java @@ -409,4 +409,9 @@ public int readVersionTag() throws IOException { return 0; // versioning not supported for minbin } + @Override + public void pushBack(int bytes) { + input.setPos(input.getPos()-bytes); + } + } diff --git a/src/main/java/org/nustaq/serialization/coders/FSTStreamDecoder.java b/src/main/java/org/nustaq/serialization/coders/FSTStreamDecoder.java index 4c94b37a..97a78e76 100644 --- a/src/main/java/org/nustaq/serialization/coders/FSTStreamDecoder.java +++ b/src/main/java/org/nustaq/serialization/coders/FSTStreamDecoder.java @@ -487,6 +487,11 @@ public int readVersionTag() throws IOException { return readFByte(); } + @Override + public void pushBack(int bytes) { + input.pos -= bytes; + } + } diff --git a/src/main/java/org/nustaq/serialization/minbin/MBIn.java b/src/main/java/org/nustaq/serialization/minbin/MBIn.java index c9278d0f..3a4224d2 100644 --- a/src/main/java/org/nustaq/serialization/minbin/MBIn.java +++ b/src/main/java/org/nustaq/serialization/minbin/MBIn.java @@ -185,6 +185,10 @@ public int getPos() { return pos; } + public void setPos(int newpos) { + pos = newpos; + } + public void setBuffer(byte[] buf, int count) { this.bytez = buf; pos = 0; diff --git a/src/main/web/minbin/minbin-2.20.js b/src/main/web/minbin/minbin-2.20.js new file mode 100644 index 00000000..54c2e942 --- /dev/null +++ b/src/main/web/minbin/minbin-2.20.js @@ -0,0 +1,955 @@ +/* + * Copyright 2014 Ruediger Moeller. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// missing: floatarr tag ser impl +// refs only on string, sequence, object. native arrays missing +// detect cycles in prettyprinter + +var END_MARKER = "_E_"; + +// low 3 bits contain type +var INT_8 = 1; // 0x0001 (17 = array) +var INT_16 = 2; // 2 (18 ..) +var INT_32 = 3; // 3 (19 ..) +var INT_64 = 4; // 4 (20 ..) +var TAG = 5; // 5, top 5 bits contains tag id +var END = 6; // 6, end marker +var RESERV = 7; // 7 escape for future extension + +var UNSIGN_MASK = 8; // int only. if true => unsigned .. +var ARRAY_MASK = 16; // int only, next item expected to be length, then plain data little endian +var CHAR = UNSIGN_MASK | INT_16; + +// predefined tag id's +var STRING = 0; +var FLOAT = 1; +var DOUBLE = 2; +var DOUBLE_ARR = 3; +var FLOAT_ARR = 4; +var OBJECT = 5; +var SEQUENCE = 6; +var NULL = 7; +var BOOL = 8; +var HANDLE = 9; // not supported in 1.0 + +// dummy impl +var mbfactory = function(clazz,obj) { + obj.__typeInfo = clazz; + return obj; +}; + +var MinBin = new function MinBin() { + + // public API => + +// this.setKnownNames = function(listOfAttributeNames) { +// this.nameHash = {}; +// for ( var i = 0; i < listOfAttributeNames.length; i++) { +// this.nameHash[listOfAttributeNames[i],i]; +// } +// }; + + this.prettyPrint = function(object) { return new MBPrinter().prettyPrintStreamObject(object, "", ""); }; + + // does not overwrite if typeinfo is already set + this.obj = function(clazz,object) { + if ( object.__typeInfo == clazz || object._actorProxy ) // do not overwrite fqname of actor proxies + return object; + if ( object.__typeInfo && object.__typeInfo.indexOf('.') < 0 ) + return object; // already converted + return mbfactory(clazz,object); + }; + + this.strArr = function(array) { + var res = []; + res.__typeInfo = "String[]"; + for ( var i = 0; i < array.length; i++) { + res.push(array[i]); + } + return res; + }; + + // if type is give ('int' 'byte' 'short' ..) an object is returned + this.parseIntOrNan = function(number, type) { + var tmp = parseInt(number,10); + if ( isNaN(tmp) ) { + tmp = 0; + } + if (type) { + tmp = { __typeInfo: type, value: tmp, _isJNum: true }; + } + return tmp; + }; + + this.i32 = function(array) { + var res = new Int32Array(array.length); + for ( var i = 0; i < array.length; i++) { + res[i] = array[i]; + } + return res; + }; + + this.i8 = function(array) { + var res = new Int8Array(array.length); + if ( typeof array === 'string' ) { + for ( var i = 0; i < array.length; i++) { + res[i] = array.charCodeAt(i); + } + } else { + for ( var i = 0; i < array.length; i++) { + res[i] = array[i]; + } + } + return res; + }; + + this.i16 = function(array) { + var res = new Int16Array(array.length); + for ( var i = 0; i < array.length; i++) { + res[i] = array[i]; + } + return res; + }; + + this.ui16 = function(array) { + var res = new Uint16Array(array.length); + for ( var i = 0; i < array.length; i++) { + res[i] = array[i]; + } + return res; + }; + + // collections + this.jlist = function(array) { + var res = [array.length]; + for ( var i = 0; i < array.length; i++) { + res.push(array[i]); + } + res.__typeInfo = "list"; + return res; + }; + + // object array + this.jarray = function(array) { + array.__typeInfo = "array"; + return array; + }; + + this.jmap = function(map) { + if ( ! map ) + return null; + if( Object.prototype.toString.call( map ) === '[object Array]' ) { + return map;// already converted + } + var res = [Object.keys(map).length]; + for ( var key in map) { + res.push(key); + res.push(map[key]); + } + res.__typeInfo = "map"; + return res; + }; + + this.installFactory = function( fun ) { + this.serializer[OBJECT].objectFactory = fun; + }; + + this.decode = function (int8bufferOrString) { + if ( int8bufferOrString instanceof ArrayBuffer ) + int8bufferOrString = String.fromCharCode.apply(null, new Uint8Array(int8bufferOrString)); + else if ( this.isBuffer(int8bufferOrString) ) + int8bufferOrString = String.fromCharCode.apply(null, new Uint16Array(int8bufferOrString)); + this.MBin.objectMap = {}; + this.MBin.pos = 0; + this.MBin.bytez = int8bufferOrString; + var res = this.MBin.readObject(); + this.MBin.objectMap = {}; + return res; + }; + + this.encode = function (object) { + this.MBOut.reset(); + this.MBOut.writeObject(object); + var res = new Int8Array(this.MBOut.pos); + var from = this.MBOut.bytez; + for ( var i=0; i < res.length; i++) { + res[i] = from[i]; + } + return res; +// return new Int8Array(this.MBOut.bytez, 0, this.MBOut.pos); buggy impl in chrome + }; + + // END public API + + this.serializer = []; + this.nameHash = {}; + + this.serializer[STRING] = new StringTagSer(); + this.serializer[NULL] = new NullTagSer(); + this.serializer[OBJECT] = new MBObjectTagSer(); + this.serializer[SEQUENCE] = new MBSequenceTagSer(); + this.serializer[FLOAT] = new FloatTagSer(); + this.serializer[DOUBLE] = new FloatTagSer(); + this.serializer[FLOAT_ARR] = new FloatArrTagSer(); + this.serializer[DOUBLE_ARR] = new FloatArrTagSer(); + this.serializer[BOOL] = new BigBoolTagSer(); + this.serializer[HANDLE] = new RefTagSer(); + + this.serializer[STRING].tagId = STRING; + this.serializer[NULL].tagId = NULL; + this.serializer[OBJECT].tagId = OBJECT; + this.serializer[SEQUENCE].tagId = SEQUENCE; + this.serializer[FLOAT].tagId = FLOAT; + this.serializer[DOUBLE].tagId = DOUBLE; + this.serializer[FLOAT_ARR].tagId = FLOAT_ARR; + this.serializer[DOUBLE_ARR].tagId = DOUBLE_ARR; + this.serializer[BOOL].tagId = BOOL; + this.serializer[HANDLE].tagId = HANDLE; + + this.getTagSerializerFor = function (obj) { + if ( obj && obj._actorProxy ) { + return this.serializer[SEQUENCE]; + } + if ( obj instanceof String || typeof obj == "string" ) + return this.serializer[STRING]; + if ( obj == null ) + return this.serializer[NULL]; + if ( obj instanceof Array ) { + if ( !obj.__typeInfo ) + obj.__typeInfo = 'array'; + return this.serializer[SEQUENCE]; + // check for int array + } + if ( obj instanceof Object && ! this.isBuffer(obj) ) + return this.serializer[OBJECT]; + if ( this.isFloat(obj) ) + return this.serializer[DOUBLE]; + if ( typeof obj == 'boolean' ) + return this.serializer[BOOL]; + return null; + }; + + this.isFloat = function(n) { return typeof n === 'number' && (n % 1) != 0; }; + + this.isInteger = function(n) { return n === (n|0); }; + + /** return wether type is primitive or primitive array */ + this.isPrimitive = function (type) { return (type & RESERV) < TAG; }; + + /** return wether type is a tag */ + this.isTag = function (type) { return (type & 7) == TAG; }; + + /** extract tag id/nr from byte */ + this.getTagId = function(type) { return (type >>> 3); }; + + /** get tag code as written to stream from tag id */ + this.getTagCode = function(tagId) { return ((tagId << 3) | TAG); }; + + /** is primitive type signed ? */ + this.isSigned = function(type) { return (type & 7) < TAG && (type & UNSIGN_MASK) == 0; }; + + /** is primitive and array array */ + this.isArray = function(type) { return (type & 7) < TAG && (type & ARRAY_MASK) != 0; }; + + this.extractNumBytes = function(type) { return (1 << ((type & 7) - 1)); }; + + this.getBaseType = function(type) { return ((type & 7) | (type & UNSIGN_MASK)); }; + + this.isBuffer = function(value) { + return value && value.buffer instanceof ArrayBuffer && value.byteLength !== undefined; + }; + + this.str2ab8 = function(str) { + var bufView = new Int8Array(str.length); + for (var i=0, strLen=str.length; i>> 8; + } + }; + + /** + * encode int using only as much bytes as needed to represent it + * @param data + */ + this.writeIntPacked = function(data) { + if (data <= 127 && data >= -128) this.writeInt(INT_8, data); + else if (data <= 32767 && data >= -32768) this.writeInt(INT_16, data); + else if (data <= 0x7fffffff && data >= -0x80000000) this.writeInt(INT_32, data); + else throw "long not supported from js, number overflow"; + }; + + /** + * write primitive array + header. no floating point or object array allowed. Just int based types + * @param primitiveArray + * @param start + * @param len + */ + this.writeArray = function(primitiveArray, start, len) { + var type = ARRAY_MASK; + if (primitiveArray instanceof Int8Array) type |= INT_8; + else if (primitiveArray instanceof Int16Array) type |= INT_16; + else if (primitiveArray instanceof Uint16Array) type |= INT_16 | UNSIGN_MASK; + else if (primitiveArray instanceof Int32Array) type |= INT_32; + else throw "unsupported type ".concat(primitiveArray); + this.writeOut(type); + this.writeIntPacked(len); + for (var i = start; i < start + len; i++) { + this.writeRawInt(type, primitiveArray[i]); + } + }; + + this.writeTagHeader = function(tagId) { + this.writeOut(MinBin.getTagCode(tagId)); + }; + + this.writeTag = function( obj ) { + if (obj==END_MARKER) { + this.writeOut(END); + return; + } + var tagSerializer = MinBin.getTagSerializerFor(obj); + if ( tagSerializer == MinBin.serializer[SEQUENCE] || tagSerializer == MinBin.serializer[OBJECT] ) { + if ( !this.writeRefIfApplicable(obj) ) + return; + } + if ( tagSerializer == null ) { + throw "no tag serializer found for "+obj; + } + this.writeTagHeader(tagSerializer.tagId); + tagSerializer.writeTag(obj,this); + }; + + this.getWritten = function() { return this.pos; }; + this.getBytez = function() { return this.bytez; }; + + /** + * completely reset state + */ + this.reset = function() { + this.pos = 0; +// this.__idcnt = 1; + this.objId2pos = {}; + this.string2Id = {}; + }; + + this.writeObject = function(o) { + if ( o == null ) { + this.writeTag(o); + } else if (o._isJNum) { + if (o.__typeInfo) { + switch(o.__typeInfo) { + case 'byte': this.writeInt(INT_8, o.value);break; + case 'char': this.writeInt(CHAR, o.value);break; + case 'short': this.writeInt(INT_16,o.value);break; + case 'int': this.writeInt(INT_32,o.value);break; + default : + console.log('unhandled int type:'+ o.__typeInfo); + this.writeIntPacked(o.value); + } + } else + throw "Wat is denn nur los ?"; + } else if ( MinBin.isInteger(o) ) { + this.writeIntPacked(o); + } else if ( MinBin.isBuffer(o) ) { + if ( this.writeRefIfApplicable(o) ) + this.writeArray( o, 0, o.length ); + } else if (o instanceof MBLong){ + this.writeOut(INT_64); + this.writeRawInt( INT_32, o.loInt ); + this.writeRawInt( INT_32, o.hiInt ); + } else { + this.writeTag(o); + } + }; +} + + +function MBIn(rawmsg) { + + this.bytez = rawmsg; // Int8Array + this.pos = 0; + + this.peekIn = function() { return this.bytez.charCodeAt(this.pos); }; + + this.readIn = function() { return this.bytez.charCodeAt(this.pos++); }; + + this.readRawInt = function(type) { + var res = 0; + var numBytes = MinBin.extractNumBytes(type); + if ( numBytes == 8 ) // special handling for long + { + var lng = new MBLong(); + lng.loInt = ((this.readIn()+256) & 0xff); + lng.loInt += ((this.readIn()+256) & 0xff)<<8; + lng.loInt += ((this.readIn()+256) & 0xff)<<16; + lng.loInt += ((this.readIn()+256) & 0xff)<<24; + lng.hiInt = ((this.readIn()+256) & 0xff); + lng.hiInt += ((this.readIn()+256) & 0xff)<<8; + lng.hiInt += ((this.readIn()+256) & 0xff)<<16; + lng.hiInt += ((this.readIn()+256) & 0xff)<<24; + return lng; + } + var shift = 0; + for ( var i = 0; i < numBytes; i++ ) { + var b = (this.readIn()+256) & 0xff; + res += b<= 128 ) l-=256; return l; + case 2: l = (l&0xffff); if ( l >= 32768 ) l-=65536; return l; + case 4: l = (l&0xffffffff); if ( l >= 0x80000000 ) l-=0xffffffff+1; return l; + default: throw "Wat? ".concat(numBytes); + } + } + return l; + }; + + this.readArray = function() { + var type = this.readIn(); + if ( ! MinBin.isArray(type) || ! MinBin.isPrimitive(type) ) + throw "not a primitive array "+type; + var len = this.readInt(); + var baseType = MinBin.getBaseType(type); + var result; + switch (baseType) { + case INT_8: result = new Int8Array(len); break; + case INT_16: result = new Int16Array(len); break; + case CHAR: result = new Uint16Array(len); break; + case INT_32: result = new Int32Array(len); break; + case INT_64: + result = []; + for ( var i = 0; i < len; i++ ) { + var l = new MBLong(); + l.loInt = this.readRawInt(INT_32); + l.hiInt = this.readRawInt(INT_32); + result.push(l); + } + result.__typeInfo = "longarr"; + return result; + default: + throw "unknown array type"; + } + return this.readArrayRaw(type, len, result); + }; + + this.readArrayRaw = function(type,len,array) { + for ( var i = 0; i < len; i++ ) { + var val = this.readRawInt(type); + array[i] = val; + } + return array; + }; + + this.readTag = function( tag ) { + var tagId = MinBin.getTagId(tag); + var ts = MinBin.serializer[tagId]; + if (ts==null) { + throw "no tagser found ".concat(tagId); + } + return ts.readTag(this); + }; + + this.readObject = function() { + var type = this.peekIn(); + if (type==END) { + this.readIn(); + return END_MARKER; + } + if ( MinBin.isPrimitive(type) ) { + if ( MinBin.isArray(type) ) { + return this.readArray(); + } + switch (type) { + case INT_8: return this.readInt(); + case INT_16: return this.readInt(); + case CHAR: return this.readInt(); + case INT_32: return this.readInt(); + case INT_64: return this.readInt(); + default: throw "unexpected primitive type:"; + } + } else { + return this.readTag(this.readIn()); + } + } + +} + + +function MBRef(pos) { + this.pos = pos; +} + +function MBLong() { + this.loInt = 0; + this.hiInt = 0; +} + +function StringTagSer() { + + this.writeTag = function(data, out) { + var s = data; + var isAsc = s.length < 64; + if (isAsc) { + for (var i = 0; i < s.length; i++) { + if (s.charCodeAt(i) >= 127) { + isAsc = false; + break; + } + } + } + if ( isAsc ) + out.writeOut(INT_8|ARRAY_MASK); + else + out.writeOut(CHAR|ARRAY_MASK); + out.writeIntPacked(s.length); + for (var i = 0; i < s.length; i++) { + if (isAsc) + out.writeOut(data.charCodeAt(i)); + else + out.writeRawInt(CHAR,data.charCodeAt(i)); + } + }; + + this.readTag = function(inp) { + var objpos = inp.pos; + var arr = inp.readArray(); + var res = ''; + for (var i = 0; i < arr.length; i++) { + res += String.fromCharCode(arr[i]); + } + inp.objectMap[objpos] = res; + return res; + }; + +} + +function NullTagSer() { + this.writeTag = function(data, out) {}; + this.readTag = function(inp) { return null; }; +} + +function MBObjectTagSer() { + /** + * tag is already written. break down the given object into more tags or primitives + * + * @param data + * @param out + */ + this.writeTag = function(data, out) { + var name = data.__typeInfo; + if ( typeof name === "undefined" ) { + name = data.constructor.name; + } + out.writeObject(name); + + var count = 0; + for (var next in data ) { + if (data.hasOwnProperty(next) && next != "__typeInfo" && next != "__idcnt" ) { + var val = data[next]; + if ( typeof val != 'function' ) + count++; + } + } + out.writeIntPacked(count); + for (var next in data ) { + if (data.hasOwnProperty(next) && next != "__typeInfo" && next != "__idcnt" ) { + var val = data[next]; + if ( data.hasOwnProperty('j_'+next) ) { + val = data['j_'+next](); + } + if ( typeof val != 'function' ) { + out.writeObject(next); + if (out.writeRefIfApplicable(val)) { + out.writeObject(val); + } + } + } + } + }; + + this.objectFactory = function(clazzName) { + return { "__typeInfo" : clazzName }; + }; + + /** + * tag is already read, reconstruct the object + * + * @param in + * @return + */ + this.readTag = function(inp) { + var objpos = inp.pos-1; + var typeInfo = inp.readObject(); + var len = inp.readInt(); + var obj = this.objectFactory(typeInfo); + + for ( var i=0; i < len || len < 0 ; i++ ) { + var key = inp.readObject(); + if (key==END_MARKER) + break; + obj[key] = inp.readObject(); + } + inp.objectMap[objpos] = obj; + return obj; + }; +} + +var mbendsWith = function endsWith(str, suffix) { + return str.indexOf(suffix, str.length - suffix.length) !== -1; +}; + + +function MBSequenceTagSer() { + + this.writeTag = function(data, out) { + out.writeTag(data.__typeInfo); + if ( data._actorProxy ) { // locally implemented actor sent or foreign ref sent + out.writeIntPacked(2); + out.writeObject( data._foreignRefKey ? data._foreignRefKey : data.receiverKey); + out.writeObject( data.__typeInfo ); + return; + } + out.writeIntPacked(data.length); + for (var i = 0; i < data.length; i++) { + if ( out.writeRefIfApplicable(data[i]) ) + out.writeObject(data[i]); + } + }; + this.readTag = function (inp) { + var objpos = inp.pos-1; + var typeInfo = inp.readObject(); + var len = inp.readInt(); + var arr = []; + arr["__typeInfo"] = typeInfo; + for ( var i=0; i < len || len < 0; i++ ) { + var o = inp.readObject(); + if ( o == END_MARKER ) + break; + arr.push(o); + } + if ( 'map' == typeInfo ) { + var res = {}; + var isStringKey = true; + for ( var i = 1; i < arr.length; i+=2 ) { + var key = arr[i]; + if ( typeof key === 'string' ) { + var val = arr[i+1]; + res[key] = val; + } else { + isStringKey = false; + break; + } + } + if ( isStringKey ) + arr = res; + } else if ( 'list' == typeInfo ) { + var res = []; + for ( var i = 1; i < arr.length; i++ ) { + res.push(arr[i]); + } + return res; + } else { + // handle actor ref here as objectFactory is replaced by generated stuff + if (typeInfo) { + var clz = typeInfo; + if (mbendsWith(clz, "_ActorProxy")) { + var id = arr[0]; + clz = clz.substr(0, clz.length - "_ActorProxy".length); + var idx = clz.lastIndexOf("."); + if (idx >= 0) { + clz = clz.substr(idx + 1); + } + idx = clz.lastIndexOf("$"); + if (idx >= 0) { + clz = clz.substr(idx + 1); + } + arr = mbfactory(clz, id); + // restore full class name + arr.__typeInfo = typeInfo; + } + } + // end handling actor ref + } + inp.objectMap[objpos] = arr; + return arr; + }; +} + +function FloatTagSer() { + this.writeTag = function(data, out) { + data = ''.concat(data); + var bytes = MinBin.str2ab8(data); + out.writeArray(bytes, 0, bytes.length); + }; + this.readTag = function(inp) { + var ba = inp.readArray(); + var s = String.fromCharCode.apply(null, new Uint16Array(ba)); + return parseFloat(s); + }; +} + +function BigBoolTagSer() { + this.writeTag = function(data,out) { out.writeInt(INT_8, data?1:0); }; + this.readTag = function(inp) { return inp.readInt() != 0; }; +} + +function RefTagSer() { + this.writeTag = function(data,out) { + throw "unexpected call"; + }; + this.readTag = function(inp) { + var readpos = inp.readInt(); + if ( inp.objectMap[readpos] == null ) { + if ( inp.objectMap[readpos+1] == null ) // quirks + { + var debug = inp.objectMap; + throw "unresolvable ref ".concat(readpos); + } else + readpos++; + } + return inp.objectMap[readpos]; +// return new MBRef(readpos); + }; +} + +function FloatArrTagSer() { + + this.writeTag = function(data, out) { +// var d[] = []; +// out.writeIntPacked(((double[]) data).length); +// for (int i = 0; i < d.length; i++) { +// byte[] bytes = Float.toString(d[i]).getBytes(); +// out.writeArray(bytes, 0, bytes.length); +// } + }; + + this.readTag = function(inp) { + var len = inp.readInt(); + var res = []; + for (var i = 0; i < len; i++) { + var ba = inp.readArray(); + var s = String.fromCharCode.apply(null, new Uint16Array(ba)); + res.push( parseFloat(s) ); + } + return res; + }; +} + +function MBPrinter(object) { + + this.visited = []; + + this.prettyPrintStreamObject = function( o, out, indent ) { + if (o instanceof MBRef) { + return "#".concat(o.pos); + } else + if ( o instanceof MBLong) { + return this.objectToString(o); + } else if ( o instanceof Array ) { + return this.prettyPrintSequence(o, out, indent.concat(" ")); + } else if (o instanceof Object && ! MinBin.isBuffer(o) ) { + return this.prettyPrintObject(o, out, indent.concat(" ")); + } else + return this.objectToString(o); + }; + + this.prettyPrintObject = function(t, out, indent) { + if (this.visited.indexOf(t) >= 0) { + out += "#ref"; + return; + } + this.visited.push(t); + out = this.prettyPrintStreamObject(t.__typeInfo,out,indent); + out = out.concat(" {\n"); + for (var next in t ) { + if (t.hasOwnProperty(next) && next != "__typeInfo" && next != "__idcnt" && typeof t[next] != 'function' ) { + out = out.concat( indent+" " ); + out = out.concat( this.prettyPrintStreamObject(next, out, indent) ); + out = out.concat(" : "); + out = out.concat( this.prettyPrintStreamObject( t[next], "", indent ) ); + out = out.concat("\n"); + } + } + out = out.concat(indent.concat( "}") ); + return out; + }; + + this.prettyPrintSequence = function(t, out, indent) { + if (this.visited.indexOf(t) >= 0) { + out += "#ref"; + return; + } + this.visited.push(t); + out = out.concat(this.prettyPrintStreamObject(t.__typeInfo,out,indent)); + out = out.concat(" [\n"); + for (var i = 0; i < t.length; i++) { + out = out.concat(indent.concat(" ")); + out = out.concat(this.prettyPrintStreamObject(t[i], "", indent)); + out = out.concat("\n"); + } + out = out.concat(indent.concat( "]") ); + return out; + }; + + this.arrayToString = function(o) { + var len = o.length; + var res = "[ "; + for (var i = 0; i < len; i++) { + res = res.concat(o[i]); + if ( i < len-1) + res=res.concat(","); + } + res = res.concat(" ]"); + return res; + }; + + this.objectToString = function(o) { + if ( o instanceof MBLong ) { + return "L[".concat(o.hiInt).concat(",").concat(o.loInt).concat("]"); + } + if ( o == null ) { + return "NULL"; + } + if ( o instanceof Array || MinBin.isBuffer(o)) + return this.arrayToString(o); + if ( o instanceof String || typeof o == "string") { + return "\"".concat(o).concat("\""); + } + return "".concat(o); + }; + +} + diff --git a/src/test/ser/issue52/I52.java b/src/test/ser/issue52/I52.java new file mode 100644 index 00000000..43688de0 --- /dev/null +++ b/src/test/ser/issue52/I52.java @@ -0,0 +1,42 @@ +package ser.issue52; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Test; +import org.nustaq.serialization.FSTConfiguration; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.RSAKeyGenParameterSpec; + +/** + * Created by ruedi on 01/02/15. + */ +public class I52 { + + // Fermat F4, largest known fermat prime + private static final BigInteger PUBLIC_EXP = new BigInteger("10001", 16);; + private static final int STRENGTH = 1024; + + @Test + public void test() throws Exception { + // install BouncyCastle provider + Security.addProvider(new BouncyCastleProvider()); + + // generate a keypair + KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "BC"); + RSAKeyGenParameterSpec params = new RSAKeyGenParameterSpec(STRENGTH, PUBLIC_EXP); + gen.initialize(params, new SecureRandom()); + KeyPair keyPair = gen.generateKeyPair(); + + FSTConfiguration fst = FSTConfiguration.createDefaultConfiguration(); + + // serialize + byte[] serialized = fst.asByteArray(keyPair); + + // deserialize --> crash + KeyPair deserialized = (KeyPair) fst.asObject(serialized); + } +}