diff options
Diffstat (limited to 'js/src/Ice/Stream.js')
-rw-r--r-- | js/src/Ice/Stream.js | 3515 |
1 files changed, 3515 insertions, 0 deletions
diff --git a/js/src/Ice/Stream.js b/js/src/Ice/Stream.js new file mode 100644 index 00000000000..6dc5c613d7d --- /dev/null +++ b/js/src/Ice/Stream.js @@ -0,0 +1,3515 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +const Ice = require("../Ice/ModuleRegistry").Ice; +const _ModuleRegistry = Ice._ModuleRegistry; +_ModuleRegistry.require(module, + [ + "../Ice/Debug", + "../Ice/ExUtil", + "../Ice/FormatType", + "../Ice/Object", + "../Ice/Value", + "../Ice/OptionalFormat", + "../Ice/Protocol", + "../Ice/TraceUtil", + "../Ice/Buffer", + "../Ice/Exception", + "../Ice/LocalException", + "../Ice/Version", + "../Ice/CompactIdRegistry", + "../Ice/ArrayUtil", + "../Ice/UnknownSlicedValue" + ]); + +const Debug = Ice.Debug; +const ExUtil = Ice.ExUtil; +const FormatType = Ice.FormatType; +const OptionalFormat = Ice.OptionalFormat; +const Protocol = Ice.Protocol; +const TraceUtil = Ice.TraceUtil; +const ArrayUtil = Ice.ArrayUtil; +const SlicedData = Ice.SlicedData; + +const SliceType = +{ + NoSlice: 0, + ValueSlice: 1, + ExceptionSlice: 2 +}; + +// +// Number.isNaN polyfill for compatibility with IE +// +// see: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN +// +Number.isNaN = Number.isNaN || function(value) +{ + return typeof value === "number" && isNaN(value); +}; + +// +// InputStream +// + +class IndirectPatchEntry +{ + constructor(index, cb) + { + this.index = index; + this.cb = cb; + } +} + +class EncapsDecoder +{ + constructor(stream, encaps, sliceValues, f) + { + this._stream = stream; + this._encaps = encaps; + this._sliceValues = sliceValues; + this._valueFactoryManager = f; + this._patchMap = null; // Lazy initialized, Map<int, Patcher[] >() + this._unmarshaledMap = new Map(); // Map<int, Ice.Value>() + this._typeIdMap = null; // Lazy initialized, Map<int, String> + this._typeIdIndex = 0; + this._valueList = null; // Lazy initialized. Ice.Value[] + } + + readOptional() + { + return false; + } + + readPendingValues() + { + } + + readTypeId(isIndex) + { + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new Map(); // Map<int, String>(); + } + + let typeId; + if(isIndex) + { + typeId = this._typeIdMap.get(this._stream.readSize()); + if(typeId === undefined) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + typeId = this._stream.readString(); + this._typeIdMap.set(++this._typeIdIndex, typeId); + } + return typeId; + } + + newInstance(typeId) + { + // + // Try to find a factory registered for the specific type. + // + let userFactory = this._valueFactoryManager.find(typeId); + let v = null; + + if(userFactory !== undefined) + { + v = userFactory(typeId); + } + + // + // If that fails, invoke the default factory if one has been + // registered. + // + if(v === null || v === undefined) + { + userFactory = this._valueFactoryManager.find(""); + if(userFactory !== undefined) + { + v = userFactory(typeId); + } + } + + // + // Last chance: try to instantiate the class dynamically. + // + if(v === null || v === undefined) + { + v = this._stream.createInstance(typeId); + } + + return v; + } + + addPatchEntry(index, cb) + { + Debug.assert(index > 0); + + // + // Check if we have already unmarshaled the instance. If that's the case, + // just call the callback and we're done. + // + const obj = this._unmarshaledMap.get(index); + if(obj !== undefined && obj !== null) + { + cb.call(null, obj); + return; + } + + if(this._patchMap === null) // Lazy initialization + { + this._patchMap = new Map(); // Map<Integer, Patcher[] >(); + } + + // + // Add a patch entry if the instance isn't unmarshaled yet, + // the callback will be called when the instance is + // unmarshaled. + // + let l = this._patchMap.get(index); + if(l === undefined) + { + // + // We have no outstanding instances to be patched for this + // index, so make a new entry in the patch map. + // + l = []; // ReadValueCallback[] + this._patchMap.set(index, l); + } + + // + // Append a patch entry for this instance. + // + l.push(cb); + } + + unmarshal(index, v) + { + // + // Add the instance to the map of unmarshaled instances, this must + // be done before reading the instances (for circular references). + // + this._unmarshaledMap.set(index, v); + + // + // Read the instance. + // + v._iceRead(this._stream); + + if(this._patchMap !== null) + { + // + // Patch all instances now that the instance is unmarshaled. + // + const l = this._patchMap.get(index); + if(l !== undefined) + { + Debug.assert(l.length > 0); + + // + // Patch all pointers that refer to the instance. + // + for(let i = 0; i < l.length; ++i) + { + l[i](v); + } + + // + // Clear out the patch map for that index -- there is nothing left + // to patch for that index for the time being. + // + this._patchMap.delete(index); + } + } + + if((this._patchMap === null || this._patchMap.size === 0) && this._valueList === null) + { + try + { + v.ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_postUnmarshal:\n" + + ex.toString()); + } + } + else + { + if(this._valueList === null) // Lazy initialization + { + this._valueList = []; // Ice.Value[] + } + this._valueList.push(v); + + if(this._patchMap === null || this._patchMap.size === 0) + { + // + // Iterate over the instance list and invoke ice_postUnmarshal on + // each instance. We must do this after all instances have been + // unmarshaled in order to ensure that any instance data members + // have been properly patched. + // + for(let i = 0; i < this._valueList.length; i++) + { + try + { + this._valueList[i].ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning( + "exception raised by ice_postUnmarshal:\n" + ex.toString()); + } + } + this._valueList = []; + } + } + } +} + +class EncapsDecoder10 extends EncapsDecoder +{ + constructor(stream, encaps, sliceValues, f) + { + super(stream, encaps, sliceValues, f); + this._sliceType = SliceType.NoSlice; + } + + readValue(cb) + { + Debug.assert(cb !== null); + + // + // Instance references are encoded as a negative integer in 1.0. + // + let index = this._stream.readInt(); + if(index > 0) + { + throw new Ice.MarshalException("invalid object id"); + } + index = -index; + + if(index === 0) + { + cb.call(null, null); + } + else + { + this.addPatchEntry(index, cb); + } + } + + throwException() + { + Debug.assert(this._sliceType === SliceType.NoSlice); + + // + // User exceptions with the 1.0 encoding start with a boolean flag + // that indicates whether or not the exception has classes. + // + // This allows reading the pending instances even if some part of + // the exception was sliced. + // + const usesClasses = this._stream.readBool(); + + this._sliceType = SliceType.ExceptionSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + const mostDerivedId = this._typeId; + while(true) + { + const userEx = this._stream.createUserException(this._typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx._read(this._stream); + if(usesClasses) + { + this.readPendingValues(); + } + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + try + { + this.startSlice(); + } + catch(ex) + { + // + // An oversight in the 1.0 encoding means there is no marker to indicate + // the last slice of an exception. As a result, we just try to read the + // next type ID, which raises UnmarshalOutOfBoundsException when the + // input buffer underflows. + // + // Set the reason member to a more helpful message. + // + if(ex instanceof Ice.UnmarshalOutOfBoundsException) + { + ex.reason = "unknown exception type `" + mostDerivedId + "'"; + } + throw ex; + } + } + } + + startInstance(sliceType) + { + Debug.assert(this._sliceType === sliceType); + this._skipFirstSlice = true; + } + + endInstance(/*preserve*/) + { + // + // Read the Ice::Object slice. + // + if(this._sliceType === SliceType.ValueSlice) + { + this.startSlice(); + let sz = this._stream.readSize(); // For compatibility with the old AFM. + if(sz !== 0) + { + throw new Ice.MarshalException("invalid Object slice"); + } + this.endSlice(); + } + + this._sliceType = SliceType.NoSlice; + return null; + } + + startSlice() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._skipFirstSlice) + { + this._skipFirstSlice = false; + return this._typeId; + } + + // + // For instances, first read the type ID boolean which indicates + // whether or not the type ID is encoded as a string or as an + // index. For exceptions, the type ID is always encoded as a + // string. + // + if(this._sliceType === SliceType.ValueSlice) // For exceptions, the type ID is always encoded as a string + { + let isIndex = this._stream.readBool(); + this._typeId = this.readTypeId(isIndex); + } + else + { + this._typeId = this._stream.readString(); + } + + this._sliceSize = this._stream.readInt(); + if(this._sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return this._typeId; + } + + endSlice() + { + } + + skipSlice() + { + this._stream.traceSkipSlice(this._typeId, this._sliceType); + Debug.assert(this._sliceSize >= 4); + this._stream.skip(this._sliceSize - 4); + } + + readPendingValues() + { + let num; + do + { + num = this._stream.readSize(); + for(let k = num; k > 0; --k) + { + this.readInstance(); + } + } + while(num > 0); + + if(this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an instance, but failed + // to supply the instance. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + } + + readInstance() + { + let index = this._stream.readInt(); + let v = null; + + if(index <= 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + this._sliceType = SliceType.ValueSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + const mostDerivedId = this._typeId; + while(true) + { + // + // For the 1.0 encoding, the type ID for the base Object class + // marks the last slice. + // + if(this._typeId == Ice.Value.ice_staticId()) + { + throw new Ice.NoValueFactoryException("", mostDerivedId); + } + + v = this.newInstance(this._typeId); + + // + // We found a factory, we get out of this loop. + // + if(v) + { + break; + } + + // + // If slicing is disabled, stop unmarshaling. + // + if(!this._sliceValues) + { + throw new Ice.NoValueFactoryException("no value factory found and slicing is disabled", + this._typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Unmarshal the instance and add it to the map of unmarshaled instances. + // + this.unmarshal(index, v); + } +} + +class EncapsDecoder11 extends EncapsDecoder +{ + constructor(stream, encaps, sliceValues, f, r) + { + super(stream, encaps, sliceValues, f); + this._compactIdResolver = r; + this._current = null; + this._valueIdIndex = 1; + } + + readValue(cb) + { + const index = this._stream.readSize(); + if(index < 0) + { + throw new Ice.MarshalException("invalid object id"); + } + else if(index === 0) + { + if(cb !== null) + { + cb.call(null, null); + } + } + else if(this._current !== null && (this._current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + // + // When reading an instance within a slice and there's an + // indirect instance table, always read an indirect reference + // that points to an instance from the indirect instance table + // marshaled at the end of the Slice. + // + // Maintain a list of indirect references. Note that the + // indirect index starts at 1, so we decrement it by one to + // derive an index into the indirection table that we'll read + // at the end of the slice. + // + if(cb !== null) + { + if(this._current.indirectPatchList === null) // Lazy initialization + { + this._current.indirectPatchList = []; // IndirectPatchEntry[] + } + this._current.indirectPatchList.push(new IndirectPatchEntry(index - 1, cb)); + } + } + else + { + this.readInstance(index, cb); + } + } + + throwException() + { + Debug.assert(this._current === null); + + this.push(SliceType.ExceptionSlice); + + // + // Read the first slice header. + // + this.startSlice(); + const mostDerivedId = this._current.typeId; + while(true) + { + + const userEx = this._stream.createUserException(this._current.typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx._read(this._stream); + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + + if((this._current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) !== 0) + { + if(mostDerivedId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(mostDerivedId.substr(2)); + } + throw new Ice.UnknownUserException(mostDerivedId); + } + + this.startSlice(); + } + } + + startInstance(sliceType) + { + Debug.assert(sliceType !== undefined); + Debug.assert(this._current.sliceType !== null && this._current.sliceType === sliceType); + this._current.skipFirstSlice = true; + } + + endInstance(preserve) + { + let slicedData = null; + if(preserve) + { + slicedData = this.readSlicedData(); + } + if(this._current.slices !== null) + { + this._current.slices.length = 0; // Clear the array. + this._current.indirectionTables.length = 0; // Clear the array. + } + this._current = this._current.previous; + return slicedData; + } + + startSlice() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._current.skipFirstSlice) + { + this._current.skipFirstSlice = false; + return this._current.typeId; + } + + this._current.sliceFlags = this._stream.readByte(); + + // + // Read the type ID, for instance slices the type ID is encoded as a + // string or as an index, for exceptions it's always encoded as a + // string. + // + if(this._current.sliceType === SliceType.ValueSlice) + { + if((this._current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_COMPACT) === + Protocol.FLAG_HAS_TYPE_ID_COMPACT) // Must be checked 1st! + { + this._current.typeId = ""; + this._current.compactId = this._stream.readSize(); + } + else if((this._current.sliceFlags & (Protocol.FLAG_HAS_TYPE_ID_INDEX | + Protocol.FLAG_HAS_TYPE_ID_STRING)) !== 0) + { + this._current.typeId = + this.readTypeId((this._current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_INDEX) !== 0); + this._current.compactId = -1; + } + else + { + // + // Only the most derived slice encodes the type ID for the compact format. + // + this._current.typeId = ""; + this._current.compactId = -1; + } + } + else + { + this._current.typeId = this._stream.readString(); + this._current.compactId = -1; + } + + // + // Read the slice size if necessary. + // + if((this._current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) !== 0) + { + this._current.sliceSize = this._stream.readInt(); + if(this._current.sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + this._current.sliceSize = 0; + } + + return this._current.typeId; + } + + endSlice() + { + if((this._current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.skipOptionals(); + } + + // + // Read the indirection table if one is present and transform the + // indirect patch list into patch entries with direct references. + // + if((this._current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + let indirectionTable = []; + // + // The table is written as a sequence<size> to conserve space. + // + let length = this._stream.readAndCheckSeqSize(1); + for(let i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + + // + // Sanity checks. If there are optional members, it's possible + // that not all instance references were read if they are from + // unknown optional data members. + // + if(indirectionTable.length === 0) + { + throw new Ice.MarshalException("empty indirection table"); + } + if((this._current.indirectPatchList === null || this._current.indirectPatchList.length === 0) && + (this._current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) === 0) + { + throw new Ice.MarshalException("no references to indirection table"); + } + + // + // Convert indirect references into direct references. + // + if(this._current.indirectPatchList !== null) + { + this._current.indirectPatchList.forEach(e => + { + Debug.assert(e.index >= 0); + if(e.index >= indirectionTable.length) + { + throw new Ice.MarshalException("indirection out of range"); + } + this.addPatchEntry(indirectionTable[e.index], e.cb); + }); + this._current.indirectPatchList.length = 0; + } + } + } + + skipSlice() + { + this._stream.traceSkipSlice(this._current.typeId, this._current.sliceType); + + const start = this._stream.pos; + + if((this._current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) !== 0) + { + Debug.assert(this._current.sliceSize >= 4); + this._stream.skip(this._current.sliceSize - 4); + } + else + { + if(this._current.sliceType === SliceType.ValueSlice) + { + throw new Ice.NoValueFactoryException("no value factory found and compact format prevents slicing " + + "(the sender should use the sliced format instead)", + this._current.typeId); + } + else + { + if(this._current.typeId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(this._current.typeId.substring(2)); + } + else + { + throw new Ice.UnknownUserException(this._current.typeId); + } + } + } + + // + // Preserve this slice. + // + const info = new Ice.SliceInfo(); + info.typeId = this._current.typeId; + info.compactId = this._current.compactId; + info.hasOptionalMembers = (this._current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) !== 0; + info.isLastSlice = (this._current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) !== 0; + + const b = this._stream._buf; + const end = b.position; + let dataEnd = end; + if(info.hasOptionalMembers) + { + // + // Don't include the optional member end marker. It will be re-written by + // endSlice when the sliced data is re-written. + // + --dataEnd; + } + + b.position = start; + info.bytes = b.getArray(dataEnd - start); + b.position = end; + + if(this._current.slices === null) // Lazy initialization + { + this._current.slices = []; // Ice.SliceInfo[] + this._current.indirectionTables = []; // int[] + } + + // + // Read the indirect instance table. We read the instances or their + // IDs if the instance is a reference to an already unmarshaled + // instance. + // + + if((this._current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + let length = this._stream.readAndCheckSeqSize(1); + let indirectionTable = []; + for(let i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + this._current.indirectionTables.push(indirectionTable); + } + else + { + this._current.indirectionTables.push(null); + } + + this._current.slices.push(info); + } + + readOptional(readTag, expectedFormat) + { + if(this._current === null) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + else if((this._current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + return false; + } + + readInstance(index, cb) + { + Debug.assert(index > 0); + + let v = null; + + if(index > 1) + { + if(cb !== null) + { + this.addPatchEntry(index, cb); + } + return index; + } + + this.push(SliceType.ValueSlice); + + // + // Get the instance ID before we start reading slices. If some + // slices are skipped, the indirect instance table is still read and + // might read other instances. + // + index = ++this._valueIdIndex; + + // + // Read the first slice header. + // + this.startSlice(); + const mostDerivedId = this._current.typeId; + while(true) + { + if(this._current.compactId >= 0) + { + // + // Translate a compact (numeric) type ID into a string type ID. + // + this._current.typeId = ""; + if(this._compactIdResolver !== null) + { + try + { + this._current.typeId = this._compactIdResolver.call(null, this._current.compactId); + } + catch(ex) + { + if(!(ex instanceof Ice.LocalException)) + { + throw new Ice.MarshalException("exception in CompactIdResolver for ID " + + this._current.compactId, ex); + } + throw ex; + } + } + + if(this._current.typeId.length === 0) + { + this._current.typeId = this._stream.resolveCompactId(this._current.compactId); + } + } + + if(this._current.typeId.length > 0) + { + v = this.newInstance(this._current.typeId); + } + + if(v !== null && v !== undefined) + { + // + // We have an instance, we get out of this loop. + // + break; + } + + // + // If slicing is disabled, stop unmarshaling. + // + if(!this._sliceValues) + { + throw new Ice.NoValueFactoryException("no value factory found and slicing is disabled", + this._current.typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + + // + // If this is the last slice, keep the instance as an opaque + // UnknownSlicedValue object. + // + if((this._current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) !== 0) + { + v = new Ice.UnknownSlicedValue(mostDerivedId); + break; + } + + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Unmarshal the instance. + // + this.unmarshal(index, v); + + if(this._current === null && this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an instance, but failed + // to supply the instance. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + + if(cb !== null) + { + cb.call(null, v); + } + + return index; + } + + readSlicedData() + { + if(this._current.slices === null) // No preserved slices. + { + return null; + } + + // + // The _indirectionTables member holds the indirection table for each slice + // in _slices. + // + Debug.assert(this._current.slices.length === this._current.indirectionTables.length); + for(let i = 0; i < this._current.slices.length; ++i) + { + // + // We use the "instances" list in SliceInfo to hold references + // to the target instances. Note that the instances might not have + // been read yet in the case of a circular reference to an + // enclosing instance. + // + const table = this._current.indirectionTables[i]; + const info = this._current.slices[i]; + info.instances = []; + if(table) + { + for(let j = 0; j < table.length; ++j) + { + this.addPatchEntry(table[j], sequencePatcher(info.instances, j, Ice.Value)); + } + } + } + return new SlicedData(ArrayUtil.clone(this._current.slices)); + } + + push(sliceType) + { + if(this._current === null) + { + this._current = new EncapsDecoder11.InstanceData(null); + } + else + { + this._current = !this._current.next ? new EncapsDecoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.skipFirstSlice = false; + } +} + +EncapsDecoder11.InstanceData = class +{ + constructor(previous) + { + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.skipFirstSlice = false; + this.slices = null; // Preserved slices. Ice.SliceInfo[] + this.indirectionTables = null; // int[][] + + // Slice attributes + this.sliceFlags = 0; + this.sliceSize = 0; + this.typeId = null; + this.compactId = 0; + this.indirectPatchList = null; // Lazy initialized, IndirectPatchEntry[] + } +}; + +const sequencePatcher = function(seq, index, T) +{ + return v => + { + if(v !== null && !(v instanceof T)) + { + ExUtil.throwUOE(T.ice_staticId(), v); + } + seq[index] = v; + }; +}; + +class ReadEncaps +{ + constructor() + { + this.start = 0; + this.sz = 0; + this.encoding = null; + this.encoding_1_0 = false; + this.decoder = null; + this.next = null; + } + + reset() + { + this.decoder = null; + } + + setEncoding(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } +} + +class InputStream +{ + constructor(arg1, arg2, arg3) + { + const args = + { + instance: null, + encoding: null, + bytes: null, + buffer: null + }; + this._checkArgs([arg1, arg2, arg3], args); + this._initialize(args); + } + + _checkArgs(arr, args) + { + // + // The constructor can accept a variety of argument combinations: + // + // (<empty>) + // (communicator) + // (instance) + // (encoding) + // (array) + // (buffer) + // (communicator, encoding) + // (instance, encoding) + // (communicator, array) + // (instance, array) + // (communicator, buffer) + // (instance, buffer) + // (communicator, encoding, array) + // (instance, encoding, array) + // (communicator, encoding, buffer) + // (instance, encoding, buffer) + // (encoding, array) + // (encoding, array) + // (encoding, buffer) + // (encoding, buffer) + // + arr.forEach(arg => + { + if(arg !== null && arg !== undefined) + { + if(arg.constructor === Ice.Communicator) + { + args.instance = arg.instance; + } + else if(arg.constructor === Ice.Instance) + { + args.instance = arg; + } + else if(arg.constructor === Ice.EncodingVersion) + { + args.encoding = arg; + } + else if(arg.constructor === Ice.Buffer) + { + args.buffer = arg; + } + else if(arg.constructor === Array) + { + args.bytes = arg; + } + else + { + throw new Ice.InitializationException("unknown argument to InputStream constructor"); + } + } + }); + if(args.buffer !== null && args.bytes !== null) + { + throw new Ice.InitializationException("invalid argument to InputStream constructor"); + } + } + + _initialize(args) + { + this._instance = args.instance; + this._encoding = args.encoding; + this._encapsStack = null; + this._encapsCache = null; + this._closure = null; + this._sliceValues = true; + this._startSeq = -1; + this._sizePos = -1; + this._compactIdResolver = null; + + if(this._instance !== null) + { + if(this._encoding === null) + { + this._encoding = this._instance.defaultsAndOverrides().defaultEncoding; + } + this._traceSlicing = this._instance.traceLevels().slicing > 0; + this._valueFactoryManager = this._instance.initializationData().valueFactoryManager; + this._logger = this._instance.initializationData().logger; + } + else + { + if(this._encoding === null) + { + this._encoding = Protocol.currentEncoding; + } + this._traceSlicing = false; + this._valueFactoryManager = null; + this._logger = null; + } + + if(args.bytes !== null) + { + this._buf = new Ice.Buffer(args.bytes); + } + else if(args.buffer !== null) + { + this._buf = args.buffer; + } + else + { + this._buf = new Ice.Buffer(); + } + } + + // + // This function allows this object to be reused, rather than reallocated. + // + reset() + { + this._buf.reset(); + this.clear(); + } + + clear() + { + if(this._encapsStack !== null) + { + Debug.assert(this._encapsStack.next); + this._encapsStack.next = this._encapsCache; + this._encapsCache = this._encapsStack; + this._encapsCache.reset(); + this._encapsStack = null; + } + + this._startSeq = -1; + this._sliceValues = true; + } + + swap(other) + { + Debug.assert(this._instance === other._instance); + + [other._buf, this._buf] = [this._buf, other._buf]; + [other._encoding, this._encoding] = [this._encoding, other._encoding]; + [other._traceSlicing, this._traceSlicing] = [this._traceSlicing, other._traceSlicing]; + [other._closure, this._closure] = [this._closure, other.closure]; + [other._sliceValues, this._sliceValues] = [this._sliceValues, other._sliceValues]; + + // + // Swap is never called for InputStreams that have encapsulations being read/write. However, + // encapsulations might still be set in case marshaling or unmarshaling failed. We just + // reset the encapsulations if there are still some set. + // + this.resetEncapsulation(); + other.resetEncapsulation(); + + [other._startSeq, this._startSeq] = [this._startSeq, other._startSeq]; + [other._minSeqSize, this._minSeqSize] = [this._minSeqSize, other._minSeqSize]; + [other._sizePos, this._sizePos] = [this._sizePos, other._sizePos]; + [other._valueFactoryManager, this._valueFactoryManager] = [this._valueFactoryManager, other._valueFactoryManager]; + [other._logger, this._logger] = [this._logger, other._logger]; + [other._compactIdResolver, this._compactIdResolver] = [this._compactIdResolver, other._compactIdResolver]; + } + + resetEncapsulation() + { + this._encapsStack = null; + } + + resize(sz) + { + this._buf.resize(sz); + this._buf.position = sz; + } + + startValue() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + this._encapsStack.decoder.startInstance(SliceType.ValueSlice); + } + + endValue(preserve) + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + return this._encapsStack.decoder.endInstance(preserve); + } + + startException() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + this._encapsStack.decoder.startInstance(SliceType.ExceptionSlice); + } + + endException(preserve) + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + return this._encapsStack.decoder.endInstance(preserve); + } + + startEncapsulation() + { + let curr = this._encapsCache; + if(curr !== null) + { + curr.reset(); + this._encapsCache = this._encapsCache.next; + } + else + { + curr = new ReadEncaps(); + } + curr.next = this._encapsStack; + this._encapsStack = curr; + + this._encapsStack.start = this._buf.position; + + // + // I don't use readSize() for encapsulations, because when creating an encapsulation, + // I must know in advance how many bytes the size information will require in the data + // stream. If I use an Int, it is always 4 bytes. For readSize(), it could be 1 or 5 bytes. + // + const sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._encapsStack.sz = sz; + + const encoding = new Ice.EncodingVersion(); + encoding._read(this); + Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported. + this._encapsStack.setEncoding(encoding); + + return encoding; + } + + endEncapsulation() + { + Debug.assert(this._encapsStack !== null); + + if(!this._encapsStack.encoding_1_0) + { + this.skipOptionals(); + if(this._buf.position !== this._encapsStack.start + this._encapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + } + else if(this._buf.position !== this._encapsStack.start + this._encapsStack.sz) + { + if(this._buf.position + 1 !== this._encapsStack.start + this._encapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + + // + // Ice version < 3.3 had a bug where user exceptions with + // class members could be encoded with a trailing byte + // when dispatched with AMD. So we tolerate an extra byte + // in the encapsulation. + // + + try + { + this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + const curr = this._encapsStack; + this._encapsStack = curr.next; + curr.next = this._encapsCache; + this._encapsCache = curr; + this._encapsCache.reset(); + } + + skipEmptyEncapsulation() + { + const sz = this.readInt(); + if(sz < 6) + { + throw new Ice.EncapsulationException(); + } + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + const encoding = new Ice.EncodingVersion(); + encoding._read(this); + Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported. + + if(encoding.equals(Ice.Encoding_1_0)) + { + if(sz != 6) + { + throw new Ice.EncapsulationException(); + } + } + else + { + // Skip the optional content of the encapsulation if we are expecting an + // empty encapsulation. + this._buf.position = this._buf.position + sz - 6; + } + return encoding; + } + + readEncapsulation(encoding) + { + Debug.assert(encoding !== undefined); + const sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(encoding !== null) + { + encoding._read(this); + this._buf.position = this._buf.position - 6; + } + else + { + this._buf.position = this._buf.position - 4; + } + + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + getEncoding() + { + return this._encapsStack !== null ? this._encapsStack.encoding : this._encoding; + } + + getEncapsulationSize() + { + Debug.assert(this._encapsStack !== null); + return this._encapsStack.sz - 6; + } + + skipEncapsulation() + { + const sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + const encoding = new Ice.EncodingVersion(); + encoding._read(this); + try + { + this._buf.position = this._buf.position + sz - 6; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return encoding; + } + + startSlice() // Returns type ID of next slice + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + return this._encapsStack.decoder.startSlice(); + } + + endSlice() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + this._encapsStack.decoder.endSlice(); + } + + skipSlice() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.decoder !== null); + this._encapsStack.decoder.skipSlice(); + } + + readPendingValues() + { + if(this._encapsStack !== null && this._encapsStack.decoder !== null) + { + this._encapsStack.decoder.readPendingValues(); + } + else if((this._encapsStack !== null && this._encapsStack.encoding_1_0) || + (this._encapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no instances were read, we + // still read an empty sequence of pending instances if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no instances + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.skipSize(); + } + } + + readSize() + { + try + { + const b = this._buf.get(); + if(b === 255) + { + const v = this._buf.getInt(); + if(v < 0) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return v; + } + return b; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readAndCheckSeqSize(minSize) + { + const sz = this.readSize(); + + if(sz === 0) + { + return sz; + } + + // + // The _startSeq variable points to the start of the sequence for which + // we expect to read at least _minSeqSize bytes from the stream. + // + // If not initialized or if we already read more data than _minSeqSize, + // we reset _startSeq and _minSeqSize for this sequence (possibly a + // top-level sequence or enclosed sequence it doesn't really matter). + // + // Otherwise, we are reading an enclosed sequence and we have to bump + // _minSeqSize by the minimum size that this sequence will require on + // the stream. + // + // The goal of this check is to ensure that when we start unmarshaling + // a new sequence, we check the minimal size of this new sequence against + // the estimated remaining buffer size. This estimatation is based on + // the minimum size of the enclosing sequences, it's _minSeqSize. + // + if(this._startSeq === -1 || this._buf.position > (this._startSeq + this._minSeqSize)) + { + this._startSeq = this._buf.position; + this._minSeqSize = sz * minSize; + } + else + { + this._minSeqSize += sz * minSize; + } + + // + // If there isn't enough data to read on the stream for the sequence (and + // possibly enclosed sequences), something is wrong with the marshaled + // data: it's claiming having more data that what is possible to read. + // + if(this._startSeq + this._minSeqSize > this._buf.limit) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return sz; + } + + readBlob(sz) + { + if(this._buf.remaining < sz) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readOptional(tag, expectedFormat) + { + Debug.assert(this._encapsStack !== null); + if(this._encapsStack.decoder !== null) + { + return this._encapsStack.decoder.readOptional(tag, expectedFormat); + } + return this.readOptImpl(tag, expectedFormat); + } + + readOptionalHelper(tag, format, read) + { + if(this.readOptional(tag, format)) + { + return read.call(this); + } + else + { + return undefined; + } + } + + readByte() + { + try + { + return this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readByteSeq() + { + return this._buf.getArray(this.readAndCheckSeqSize(1)); + } + + readBool() + { + try + { + return this._buf.get() === 1; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readShort() + { + try + { + return this._buf.getShort(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readInt() + { + try + { + return this._buf.getInt(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readLong() + { + try + { + return this._buf.getLong(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readFloat() + { + try + { + return this._buf.getFloat(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readDouble() + { + try + { + return this._buf.getDouble(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readString() + { + const len = this.readSize(); + if(len === 0) + { + return ""; + } + // + // Check the buffer has enough bytes to read. + // + if(this._buf.remaining < len) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + try + { + return this._buf.getString(len); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + readProxy(type) + { + return this._instance.proxyFactory().streamToProxy(this, type); + } + + readOptionalProxy(tag, type) + { + if(this.readOptional(tag, OptionalFormat.FSize)) + { + this.skip(4); + return this.readProxy(type); + } + else + { + return undefined; + } + } + + readEnum(T) + { + let v; + if(this.getEncoding().equals(Ice.Encoding_1_0)) + { + if(T.maxValue < 127) + { + v = this.readByte(); + } + else if(T.maxValue < 32767) + { + v = this.readShort(); + } + else + { + v = this.readInt(); + } + } + else + { + v = this.readSize(); + } + + const e = T.valueOf(v); + if(e === undefined) + { + throw new Ice.MarshalException("enumerator value " + v + " is out of range"); + } + return e; + } + + readOptionalEnum(tag, T) + { + if(this.readOptional(tag, OptionalFormat.Size)) + { + return this.readEnum(T); + } + else + { + return undefined; + } + } + + readValue(cb, T) + { + this.initEncaps(); + // + // BUGFIX: + // With Chrome on Linux the invocation of readValue on the decoder sometimes + // calls InputStream.readValue with the decoder object as this param. + // Use call instead of directly invoking the method to workaround this bug. + // + this._encapsStack.decoder.readValue.call( + this._encapsStack.decoder, + obj => + { + if(obj !== null && !(obj instanceof T)) + { + ExUtil.throwUOE(T.ice_staticId(), obj); + } + cb(obj); + }); + } + + readOptionalValue(tag, cb, T) + { + if(this.readOptional(tag, OptionalFormat.Class)) + { + this.readValue(cb, T); + } + else + { + cb(undefined); + } + } + + throwException() + { + this.initEncaps(); + this._encapsStack.decoder.throwException(); + } + + readOptImpl(readTag, expectedFormat) + { + if(this.isEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + while(true) + { + if(this._buf.position >= this._encapsStack.start + this._encapsStack.sz) + { + return false; // End of encapsulation also indicates end of optionals. + } + + const v = this.readByte(); + + if(v === Protocol.OPTIONAL_END_MARKER) + { + this._buf.position -= 1; // Rewind. + return false; + } + + const format = OptionalFormat.valueOf(v & 0x07); // First 3 bits. + let tag = v >> 3; + if(tag === 30) + { + tag = this.readSize(); + } + + if(tag > readTag) + { + const offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind + this._buf.position -= offset; + return false; // No optional data members with the requested tag. + } + else if(tag < readTag) + { + this.skipOptional(format); // Skip optional data members + } + else + { + if(format !== expectedFormat) + { + throw new Ice.MarshalException("invalid optional data member `" + tag + "': unexpected format"); + } + return true; + } + } + } + + skipOptional(format) + { + switch(format) + { + case OptionalFormat.F1: + this.skip(1); + break; + case OptionalFormat.F2: + this.skip(2); + break; + case OptionalFormat.F4: + this.skip(4); + break; + case OptionalFormat.F8: + this.skip(8); + break; + case OptionalFormat.Size: + this.skipSize(); + break; + case OptionalFormat.VSize: + this.skip(this.readSize()); + break; + case OptionalFormat.FSize: + this.skip(this.readInt()); + break; + case OptionalFormat.Class: + this.readValue(null, Ice.Value); + break; + } + } + + skipOptionals() + { + // + // Skip remaining un-read optional members. + // + while(true) + { + if(this._buf.position >= this._encapsStack.start + this._encapsStack.sz) + { + return; // End of encapsulation also indicates end of optionals. + } + + const b = this.readByte(); + const v = b < 0 ? b + 256 : b; + if(v === Protocol.OPTIONAL_END_MARKER) + { + return; + } + + const format = OptionalFormat.valueOf(v & 0x07); // Read first 3 bits. + if((v >> 3) === 30) + { + this.skipSize(); + } + this.skipOptional(format); + } + } + + skip(size) + { + if(size > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._buf.position += size; + } + + skipSize() + { + const b = this.readByte(); + if(b === 255) + { + this.skip(4); + } + } + + isEmpty() + { + return this._buf.empty(); + } + + expand(n) + { + this._buf.expand(n); + } + + createInstance(id) + { + let obj = null; + try + { + const typeId = id.length > 2 ? id.substr(2).replace(/::/g, ".") : ""; + const Class = _ModuleRegistry.type(typeId); + if(Class !== undefined) + { + obj = new Class(); + } + } + catch(ex) + { + throw new Ice.NoValueFactoryException("no value factory", id, ex); + } + + return obj; + } + + createUserException(id) + { + let userEx = null, Class; + + try + { + const typeId = id.length > 2 ? id.substr(2).replace(/::/g, ".") : ""; + const Class = _ModuleRegistry.type(typeId); + if(Class !== undefined) + { + userEx = new Class(); + } + } + catch(ex) + { + throw new Ice.MarshalException(ex); + } + return userEx; + } + + resolveCompactId(compactId) + { + const typeId = Ice.CompactIdRegistry.get(compactId); + return typeId === undefined ? "" : typeId; + } + + isEncoding_1_0() + { + return this._encapsStack !== null ? this._encapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + } + + initEncaps() + { + if(this._encapsStack === null) // Lazy initialization + { + this._encapsStack = this._encapsCache; + if(this._encapsStack !== null) + { + this._encapsCache = this._encapsCache.next; + } + else + { + this._encapsStack = new ReadEncaps(); + } + this._encapsStack.setEncoding(this._encoding); + this._encapsStack.sz = this._buf.limit; + } + + if(this._encapsStack.decoder === null) // Lazy initialization. + { + if(this._encapsStack.encoding_1_0) + { + this._encapsStack.decoder = new EncapsDecoder10(this, this._encapsStack, this._sliceValues, + this._valueFactoryManager); + } + else + { + this._encapsStack.decoder = new EncapsDecoder11(this, this._encapsStack, this._sliceValues, + this._valueFactoryManager, this._compactIdResolver); + } + } + } + + traceSkipSlice(typeId, sliceType) + { + if(this._traceSlicing && this._logger !== null) + { + TraceUtil.traceSlicing(sliceType === SliceType.ExceptionSlice ? "exception" : "object", typeId, "Slicing", + this._logger); + } + } + + // + // Sets the value factory manager to use when marshaling value instances. If the stream + // was initialized with a communicator, the communicator's value factory manager will + // be used by default. + // + get valueFactoryManager() + { + return this._valueFactoryManager; + } + + set valueFactoryManager(value) + { + this._valueFactoryManager = value !== undefined ? value : null; + } + + // + // Sets the logger to use when logging trace messages. If the stream + // was initialized with a communicator, the communicator's logger will + // be used by default. + // + get logger() + { + return this._logger; + } + + set logger(value) + { + this._logger = value !== undefined ? value : null; + } + + // + // Sets the compact ID resolver to use when unmarshaling value and exception + // instances. If the stream was initialized with a communicator, the communicator's + // resolver will be used by default. + // + get compactIdResolver() + { + return this._compactIdResolver; + } + + set compactIdResolver(value) + { + this._compactIdResolver = value !== undefined ? value : null; + } + + // + // Determines the behavior of the stream when extracting instances of Slice classes. + // A instance is "sliced" when a factory cannot be found for a Slice type ID. + // The stream's default behavior is to slice instances. + // + // If slicing is disabled and the stream encounters a Slice type ID + // during decoding for which no value factory is installed, it raises + // NoValueFactoryException. + // + get sliceValues() + { + return this._sliceValues; + } + + set sliceValues(value) + { + this._sliceValues = value; + } + + // + // Determines whether the stream logs messages about slicing instances of Slice values. + // + get traceSlicing() + { + return this._traceSlicing; + } + + set traceSlicing(value) + { + this._traceSlicing = value; + } + + get pos() + { + return this._buf.position; + } + + set pos(value) + { + this._buf.position = value; + } + + get size() + { + return this._buf.limit; + } + + get instance() + { + return this._instance; + } + + get closure() + { + return this._type; + } + + set closure(value) + { + this._type = value; + } + + get buffer() + { + return this._buf; + } +} + +// +// OutputStream +// + +class EncapsEncoder +{ + constructor(stream, encaps) + { + this._stream = stream; + this._encaps = encaps; + this._marshaledMap = new Map(); // Map<Ice.Value, int>; + this._typeIdMap = null; // Lazy initialized. Map<String, int> + this._typeIdIndex = 0; + } + + writeOptional() + { + return false; + } + + writePendingValues() + { + return undefined; + } + + registerTypeId(typeId) + { + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new Map(); // Map<String, int> + } + + const p = this._typeIdMap.get(typeId); + if(p !== undefined) + { + return p; + } + else + { + this._typeIdMap.set(typeId, ++this._typeIdIndex); + return -1; + } + } +} + +class EncapsEncoder10 extends EncapsEncoder +{ + constructor(stream, encaps) + { + super(stream, encaps); + this._sliceType = SliceType.NoSlice; + this._writeSlice = 0; // Position of the slice data members + this._valueIdIndex = 0; + this._toBeMarshaledMap = new Map(); // Map<Ice.Value, Integer>(); + } + + writeValue(v) + { + Debug.assert(v !== undefined); + // + // Object references are encoded as a negative integer in 1.0. + // + if(v !== null) + { + this._stream.writeInt(-this.registerValue(v)); + } + else + { + this._stream.writeInt(0); + } + } + + writeUserException(v) + { + Debug.assert(v !== null && v !== undefined); + // + // User exception with the 1.0 encoding start with a boolean + // flag that indicates whether or not the exception uses + // classes. + // + // This allows reading the pending instances even if some part of + // the exception was sliced. + // + const usesClasses = v._usesClasses(); + this._stream.writeBool(usesClasses); + v._write(this._stream); + if(usesClasses) + { + this.writePendingValues(); + } + } + + startInstance(sliceType) + { + this._sliceType = sliceType; + } + + endInstance() + { + if(this._sliceType === SliceType.ValueSlice) + { + // + // Write the Object slice. + // + this.startSlice(Ice.Value.ice_staticId(), -1, true); + this._stream.writeSize(0); // For compatibility with the old AFM. + this.endSlice(); + } + this._sliceType = SliceType.NoSlice; + } + + startSlice(typeId) + { + // + // For instance slices, encode a boolean to indicate how the type ID + // is encoded and the type ID either as a string or index. For + // exception slices, always encode the type ID as a string. + // + if(this._sliceType === SliceType.ValueSlice) + { + const index = this.registerTypeId(typeId); + if(index < 0) + { + this._stream.writeBool(false); + this._stream.writeString(typeId); + } + else + { + this._stream.writeBool(true); + this._stream.writeSize(index); + } + } + else + { + this._stream.writeString(typeId); + } + + this._stream.writeInt(0); // Placeholder for the slice length. + + this._writeSlice = this._stream.pos; + } + + endSlice() + { + // + // Write the slice length. + // + const sz = this._stream.pos - this._writeSlice + 4; + this._stream.rewriteInt(sz, this._writeSlice - 4); + } + + writePendingValues() + { + const writeCB = (value, key) => + { + // + // Ask the instance to marshal itself. Any new class + // instances that are triggered by the classes marshaled + // are added to toBeMarshaledMap. + // + this._stream.writeInt(value); + try + { + key.ice_preMarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning( + "exception raised by ice_preMarshal:\n" + ex.toString()); + } + key._iceWrite(this._stream); + }; + + while(this._toBeMarshaledMap.size > 0) + { + // + // Consider the to be marshalled instances as marshalled now, + // this is necessary to avoid adding again the "to be + // marshalled instances" into _toBeMarshaledMap while writing + // instances. + // + this._toBeMarshaledMap.forEach((value, key) => this._marshaledMap.set(key, value)); + + const savedMap = this._toBeMarshaledMap; + this._toBeMarshaledMap = new Map(); // Map<Ice.Value, int>(); + this._stream.writeSize(savedMap.size); + savedMap.forEach(writeCB); + } + this._stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances. + } + + registerValue(v) + { + Debug.assert(v !== null); + + // + // Look for this instance in the to-be-marshaled map. + // + let p = this._toBeMarshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // Didn't find it, try the marshaled map next. + // + p = this._marshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // We haven't seen this instance previously, create a new + // index, and insert it into the to-be-marshaled map. + // + this._toBeMarshaledMap.set(v, ++this._valueIdIndex); + return this._valueIdIndex; + } +} + +class EncapsEncoder11 extends EncapsEncoder +{ + constructor(stream, encaps) + { + super(stream, encaps); + this._current = null; + this._valueIdIndex = 1; + } + + writeValue(v) + { + Debug.assert(v !== undefined); + if(v === null) + { + this._stream.writeSize(0); + } + else if(this._current !== null && this._encaps.format === FormatType.SlicedFormat) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Value[] + this._current.indirectionMap = new Map(); // Map<Ice.Value, int> + } + + // + // If writing an instance within a slice and using the sliced + // format, write an index from the instance indirection + // table. The indirect instance table is encoded at the end of + // each slice and is always read (even if the Slice is + // unknown). + // + const index = this._current.indirectionMap.get(v); + if(index === undefined) + { + this._current.indirectionTable.push(v); + const idx = this._current.indirectionTable.length; // Position + 1 (0 is reserved for nil) + this._current.indirectionMap.set(v, idx); + this._stream.writeSize(idx); + } + else + { + this._stream.writeSize(index); + } + } + else + { + this.writeInstance(v); // Write the instance or a reference if already marshaled. + } + } + + writePendingValues() + { + return undefined; + } + + writeUserException(v) + { + Debug.assert(v !== null && v !== undefined); + v._write(this._stream); + } + + startInstance(sliceType, data) + { + if(this._current === null) + { + this._current = new EncapsEncoder11.InstanceData(null); + } + else + { + this._current = + (this._current.next === null) ? new EncapsEncoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.firstSlice = true; + + if(data !== null && data !== undefined) + { + this.writeSlicedData(data); + } + } + + endInstance() + { + this._current = this._current.previous; + } + + startSlice(typeId, compactId, last) + { + Debug.assert((this._current.indirectionTable === null || this._current.indirectionTable.length === 0) && + (this._current.indirectionMap === null || this._current.indirectionMap.size === 0)); + + this._current.sliceFlagsPos = this._stream.pos; + + this._current.sliceFlags = 0; + if(this._encaps.format === FormatType.SlicedFormat) + { + // Encode the slice size if using the sliced format. + this._current.sliceFlags |= Protocol.FLAG_HAS_SLICE_SIZE; + } + if(last) + { + this._current.sliceFlags |= Protocol.FLAG_IS_LAST_SLICE; // This is the last slice. + } + + this._stream.writeByte(0); // Placeholder for the slice flags + + // + // For instance slices, encode the flag and the type ID either as a + // string or index. For exception slices, always encode the type + // ID a string. + // + if(this._current.sliceType === SliceType.ValueSlice) + { + // + // Encode the type ID (only in the first slice for the compact + // encoding). + // + if(this._encaps.format === FormatType.SlicedFormat || this._current.firstSlice) + { + if(compactId >= 0) + { + this._current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_COMPACT; + this._stream.writeSize(compactId); + } + else + { + const index = this.registerTypeId(typeId); + if(index < 0) + { + this._current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_STRING; + this._stream.writeString(typeId); + } + else + { + this._current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_INDEX; + this._stream.writeSize(index); + } + } + } + } + else + { + this._stream.writeString(typeId); + } + + if((this._current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) !== 0) + { + this._stream.writeInt(0); // Placeholder for the slice length. + } + + this._current.writeSlice = this._stream.pos; + this._current.firstSlice = false; + } + + endSlice() + { + // + // Write the optional member end marker if some optional members + // were encoded. Note that the optional members are encoded before + // the indirection table and are included in the slice size. + // + if((this._current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.writeByte(Protocol.OPTIONAL_END_MARKER); + } + + // + // Write the slice length if necessary. + // + if((this._current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) !== 0) + { + const sz = this._stream.pos - this._current.writeSlice + 4; + this._stream.rewriteInt(sz, this._current.writeSlice - 4); + } + + // + // Only write the indirection table if it contains entries. + // + if(this._current.indirectionTable !== null && this._current.indirectionTable.length !== 0) + { + Debug.assert(this._encaps.format === FormatType.SlicedFormat); + this._current.sliceFlags |= Protocol.FLAG_HAS_INDIRECTION_TABLE; + + // + // Write the indirection instance table. + // + this._stream.writeSize(this._current.indirectionTable.length); + this._current.indirectionTable.forEach(o => this.writeInstance(o)); + this._current.indirectionTable.length = 0; // Faster way to clean array in JavaScript + this._current.indirectionMap.clear(); + } + + // + // Finally, update the slice flags. + // + this._stream.rewriteByte(this._current.sliceFlags, this._current.sliceFlagsPos); + } + + writeOptional(tag, format) + { + if(this._current === null) + { + return this._stream.writeOptImpl(tag, format); + } + + if(this._stream.writeOptImpl(tag, format)) + { + this._current.sliceFlags |= Protocol.FLAG_HAS_OPTIONAL_MEMBERS; + return true; + } + + return false; + } + + writeSlicedData(slicedData) + { + Debug.assert(slicedData !== null && slicedData !== undefined); + + // + // We only remarshal preserved slices if we are using the sliced + // format. Otherwise, we ignore the preserved slices, which + // essentially "slices" the instance into the most-derived type + // known by the sender. + // + if(this._encaps.format !== FormatType.SlicedFormat) + { + return; + } + + slicedData.slices.forEach(info => + { + this.startSlice(info.typeId, info.compactId, info.isLastSlice); + + // + // Write the bytes associated with this slice. + // + this._stream.writeBlob(info.bytes); + + if(info.hasOptionalMembers) + { + this._current.sliceFlags |= Protocol.FLAG_HAS_OPTIONAL_MEMBERS; + } + + // + // Make sure to also re-write the instance indirection table. + // + if(info.instances !== null && info.instances.length > 0) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Value[] + this._current.indirectionMap = new Map(); // Map<Ice.Value, int> + } + + + info.instances.forEach(instance => this._current.indirectionTable.push(instance)); + } + + this.endSlice(); + }); + } + + writeInstance(v) + { + Debug.assert(v !== null && v !== undefined); + + // + // If the instance was already marshaled, just write it's ID. + // + const p = this._marshaledMap.get(v); + if(p !== undefined) + { + this._stream.writeSize(p); + return; + } + + // + // We haven't seen this instance previously, create a new ID, + // insert it into the marshaled map, and write the instance. + // + this._marshaledMap.set(v, ++this._valueIdIndex); + + try + { + v.ice_preMarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_preMarshal:\n" + + ex.toString()); + } + + this._stream.writeSize(1); // Object instance marker. + v._iceWrite(this._stream); + } +} + +EncapsEncoder11.InstanceData = class +{ + constructor(previous) + { + Debug.assert(previous !== undefined); + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.firstSlice = false; + + // Slice attributes + this.sliceFlags = 0; + this.writeSlice = 0; // Position of the slice data members + this.sliceFlagsPos = 0; // Position of the slice flags + this.indirectionTable = null; // Ice.Value[] + this.indirectionMap = null; // Map<Ice.Value, int> + } +}; + +class WriteEncaps +{ + constructor() + { + this.start = 0; + this.format = FormatType.DefaultFormat; + this.encoding = null; + this.encoding_1_0 = false; + this.encoder = null; + this.next = null; + } + + reset() + { + this.encoder = null; + } + + setEncoding(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } +} + +class OutputStream +{ + constructor(arg1, arg2) + { + this._instance = null; + this._encoding = null; + + if(arg1 !== undefined && arg1 !== null) + { + if(arg1.constructor == Ice.Communicator) + { + this._instance = arg1.instance; + } + else if(arg1.constructor == Ice.Instance) + { + this._instance = arg1; + } + else if(arg1.constructor == Ice.EncodingVersion) + { + this._encoding = arg1; + } + else + { + throw new Ice.InitializationException("unknown argument to OutputStream constructor"); + } + } + + if(arg2 !== undefined && arg2 !== null) + { + if(arg2.constructor == Ice.EncodingVersion) + { + this._encoding = arg2; + } + else + { + throw new Ice.InitializationException("unknown argument to OutputStream constructor"); + } + } + + this._buf = new Ice.Buffer(); + + this._closure = null; + + this._encapsStack = null; + this._encapsCache = null; + + if(this._instance !== null) + { + if(this._encoding === null) + { + this._encoding = this._instance.defaultsAndOverrides().defaultEncoding; + } + this._format = this._instance.defaultsAndOverrides().defaultFormat; + } + else + { + if(this._encoding === null) + { + this._encoding = Protocol.currentEncoding; + } + this._format = FormatType.CompactFormat; + } + } + + // + // This function allows this object to be reused, rather than reallocated. + // + reset() + { + this._buf.reset(); + this.clear(); + } + + clear() + { + if(this._encapsStack !== null) + { + Debug.assert(this._encapsStack.next); + this._encapsStack.next = this._encapsCache; + this._encapsCache = this._encapsStack; + this._encapsCache.reset(); + this._encapsStack = null; + } + } + + finished() + { + return this.prepareWrite().getArray(this.size); + } + + swap(other) + { + Debug.assert(this._instance === other._instance); + + [other._buf, this._buf] = [this._buf, other._buf]; + [other._encoding, this._encoding] = [this._encoding, other._encoding]; + [other._closure, this._closure] = [this._closure, other._closure]; + + // + // Swap is never called for streams that have encapsulations being written. However, + // encapsulations might still be set in case marshaling failed. We just + // reset the encapsulations if there are still some set. + // + this.resetEncapsulation(); + other.resetEncapsulation(); + } + + resetEncapsulation() + { + this._encapsStack = null; + } + + resize(sz) + { + this._buf.resize(sz); + this._buf.position = sz; + } + + prepareWrite() + { + this._buf.position = 0; + return this._buf; + } + + startValue(data) + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.startInstance(SliceType.ValueSlice, data); + } + + endValue() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.endInstance(); + } + + startException(data) + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.startInstance(SliceType.ExceptionSlice, data); + } + + endException() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.endInstance(); + } + + startEncapsulation(encoding, format) + { + // + // If no encoding version is specified, use the current write + // encapsulation encoding version if there's a current write + // encapsulation, otherwise, use the stream encoding version. + // + + if(encoding === undefined) + { + if(this._encapsStack !== null) + { + encoding = this._encapsStack.encoding; + format = this._encapsStack.format; + } + else + { + encoding = this._encoding; + format = FormatType.DefaultFormat; + } + } + + Protocol.checkSupportedEncoding(encoding); + + let curr = this._encapsCache; + if(curr !== null) + { + curr.reset(); + this._encapsCache = this._encapsCache.next; + } + else + { + curr = new WriteEncaps(); + } + curr.next = this._encapsStack; + this._encapsStack = curr; + + this._encapsStack.format = format; + this._encapsStack.setEncoding(encoding); + this._encapsStack.start = this._buf.limit; + + this.writeInt(0); // Placeholder for the encapsulation length. + this._encapsStack.encoding._write(this); + } + + endEncapsulation() + { + Debug.assert(this._encapsStack); + + // Size includes size and version. + const start = this._encapsStack.start; + const sz = this._buf.limit - start; + this._buf.putIntAt(start, sz); + + const curr = this._encapsStack; + this._encapsStack = curr.next; + curr.next = this._encapsCache; + this._encapsCache = curr; + this._encapsCache.reset(); + } + + writeEmptyEncapsulation(encoding) + { + Protocol.checkSupportedEncoding(encoding); + this.writeInt(6); // Size + encoding._write(this); + } + + writeEncapsulation(v) + { + if(v.length < 6) + { + throw new Ice.EncapsulationException(); + } + this.expand(v.length); + this._buf.putArray(v); + } + + getEncoding() + { + return this._encapsStack !== null ? this._encapsStack.encoding : this._encoding; + } + + startSlice(typeId, compactId, last) + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.startSlice(typeId, compactId, last); + } + + endSlice() + { + Debug.assert(this._encapsStack !== null && this._encapsStack.encoder !== null); + this._encapsStack.encoder.endSlice(); + } + + writePendingValues() + { + if(this._encapsStack !== null && this._encapsStack.encoder !== null) + { + this._encapsStack.encoder.writePendingValues(); + } + else if((this._encapsStack !== null && this._encapsStack.encoding_1_0) || + (this._encapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no instances were written, we + // still write an empty sequence for pending instances if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no instances + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.writeSize(0); + } + } + + writeSize(v) + { + if(v > 254) + { + this.expand(5); + this._buf.put(255); + this._buf.putInt(v); + } + else + { + this.expand(1); + this._buf.put(v); + } + } + + startSize() + { + const pos = this._buf.position; + this.writeInt(0); // Placeholder for 32-bit size + return pos; + } + + endSize(pos) + { + Debug.assert(pos >= 0); + this.rewriteInt(this._buf.position - pos - 4, pos); + } + + writeBlob(v) + { + if(v === null) + { + return; + } + this.expand(v.length); + this._buf.putArray(v); + } + + // Read/write format and tag for optionals + writeOptional(tag, format) + { + Debug.assert(this._encapsStack !== null); + if(this._encapsStack.encoder !== null) + { + return this._encapsStack.encoder.writeOptional(tag, format); + } + return this.writeOptImpl(tag, format); + } + + writeOptionalHelper(tag, format, write, v) + { + if(v !== undefined) + { + if(this.writeOptional(tag, format)) + { + write.call(this, v); + } + } + } + + writeByte(v) + { + this.expand(1); + this._buf.put(v); + } + + rewriteByte(v, dest) + { + this._buf.putAt(dest, v); + } + + writeByteSeq(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this.writeSize(v.length); + this.expand(v.length); + this._buf.putArray(v); + } + } + + writeBool(v) + { + this.expand(1); + this._buf.put(v ? 1 : 0); + } + + rewriteBool(v, dest) + { + this._buf.putAt(dest, v ? 1 : 0); + } + + writeShort(v) + { + this.expand(2); + this._buf.putShort(v); + } + + writeInt(v) + { + this.expand(4); + this._buf.putInt(v); + } + + rewriteInt(v, dest) + { + this._buf.putIntAt(dest, v); + } + + writeLong(v) + { + this.expand(8); + this._buf.putLong(v); + } + + writeFloat(v) + { + this.expand(4); + this._buf.putFloat(v); + } + + writeDouble(v) + { + this.expand(8); + this._buf.putDouble(v); + } + + writeString(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this._buf.writeString(this, v); + } + } + + writeProxy(v) + { + if(v !== null) + { + v._write(this); + } + else + { + const ident = new Ice.Identity(); + ident._write(this); + } + } + + writeOptionalProxy(tag, v) + { + if(v !== undefined) + { + if(this.writeOptional(tag, OptionalFormat.FSize)) + { + const pos = this.startSize(); + this.writeProxy(v); + this.endSize(pos); + } + } + } + + writeEnum(v) + { + if(this.isEncoding_1_0()) + { + if(v.maxValue < 127) + { + this.writeByte(v.value); + } + else if(v.maxValue < 32767) + { + this.writeShort(v.value); + } + else + { + this.writeInt(v.value); + } + } + else + { + this.writeSize(v.value); + } + } + + writeValue(v) + { + this.initEncaps(); + this._encapsStack.encoder.writeValue(v); + } + + writeOptionalValue(tag, v) + { + if(v !== undefined) + { + if(this.writeOptional(tag, OptionalFormat.Class)) + { + this.writeValue(v); + } + } + } + + writeUserException(e) + { + this.initEncaps(); + this._encapsStack.encoder.writeUserException(e); + } + + writeOptImpl(tag, format) + { + if(this.isEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + let v = format.value; + if(tag < 30) + { + v |= tag << 3; + this.writeByte(v); + } + else + { + v |= 0x0F0; // tag = 30 + this.writeByte(v); + this.writeSize(tag); + } + return true; + } + + isEmpty() + { + return this._buf.empty(); + } + + expand(n) + { + this._buf.expand(n); + } + + isEncoding_1_0() + { + return this._encapsStack ? this._encapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + } + + initEncaps() + { + if(!this._encapsStack) // Lazy initialization + { + this._encapsStack = this._encapsCache; + if(this._encapsStack) + { + this._encapsCache = this._encapsCache.next; + } + else + { + this._encapsStack = new WriteEncaps(); + } + this._encapsStack.setEncoding(this._encoding); + } + + if(this._encapsStack.format === FormatType.DefaultFormat) + { + this._encapsStack.format = this._instance.defaultsAndOverrides().defaultFormat; + } + + if(!this._encapsStack.encoder) // Lazy initialization. + { + if(this._encapsStack.encoding_1_0) + { + this._encapsStack.encoder = new EncapsEncoder10(this, this._encapsStack); + } + else + { + this._encapsStack.encoder = new EncapsEncoder11(this, this._encapsStack); + } + } + } + + // + // Sets the encoding format for class and exception instances. + // + get format() + { + return this._format; + } + + set format(value) + { + this._format = value; + } + + get pos() + { + return this._buf.position; + } + + set pos(value) + { + this._buf.position = value; + } + + get size() + { + return this._buf.limit; + } + + get instance() + { + return this._instance; + } + + get closure() + { + return this._closure; + } + + set closure(value) + { + this._closure = value; + } + + get buffer() + { + return this._buf; + } +} + +const defineBuiltinHelper = function(write, read, sz, format, min, max) +{ + const helper = class + { + static write(os, v) + { + return write.call(os, v); + } + + static read(is) + { + return read.call(is); + } + + static writeOptional(os, tag, v) + { + os.writeOptionalHelper(tag, format, write, v); + } + + static readOptional(is, tag) + { + return is.readOptionalHelper(tag, format, read); + } + + static get minWireSize() + { + return sz; + } + }; + + if(min !== undefined && max !== undefined) + { + helper.validate = function(v) + { + return v >= min && v <= max; + }; + } + + return helper; +}; + +const istr = InputStream.prototype; +const ostr = OutputStream.prototype; + +// +// Constants to use in number type range checks. +// +const MIN_UINT8_VALUE = 0x0; +const MAX_UINT8_VALUE = 0xFF; + +const MIN_INT16_VALUE = -0x8000; +const MAX_INT16_VALUE = 0x7FFF; + +const MIN_UINT32_VALUE = 0x0; +const MAX_UINT32_VALUE = 0xFFFFFFFF; + +const MIN_INT32_VALUE = -0x80000000; +const MAX_INT32_VALUE = 0x7FFFFFFF; + +const MIN_FLOAT32_VALUE = -3.4028234664e+38; +const MAX_FLOAT32_VALUE = 3.4028234664e+38; + +Ice.ByteHelper = defineBuiltinHelper(ostr.writeByte, istr.readByte, 1, Ice.OptionalFormat.F1, + MIN_UINT8_VALUE, MAX_UINT8_VALUE); + +Ice.ShortHelper = defineBuiltinHelper(ostr.writeShort, istr.readShort, 2, Ice.OptionalFormat.F2, + MIN_INT16_VALUE, MAX_INT16_VALUE); + +Ice.IntHelper = defineBuiltinHelper(ostr.writeInt, istr.readInt, 4, Ice.OptionalFormat.F4, + MIN_INT32_VALUE, MAX_INT32_VALUE); + +Ice.FloatHelper = defineBuiltinHelper(ostr.writeFloat, istr.readFloat, 4, Ice.OptionalFormat.F4, + MIN_FLOAT32_VALUE, MAX_FLOAT32_VALUE); +Ice.FloatHelper.validate = function(v) +{ + return Number.isNaN(v) || v == Number.POSITIVE_INFINITY || v == Number.NEGATIVE_INFINITY || + (v >= MIN_FLOAT32_VALUE && v <= MAX_FLOAT32_VALUE); +}; + +Ice.DoubleHelper = defineBuiltinHelper(ostr.writeDouble, istr.readDouble, 8, Ice.OptionalFormat.F8, + -Number.MAX_VALUE, Number.MAX_VALUE); +Ice.DoubleHelper.validate = function(v) +{ + return Number.isNaN(v) || v == Number.POSITIVE_INFINITY || v == Number.NEGATIVE_INFINITY || + (v >= -Number.MAX_VALUE && v <= Number.MAX_VALUE); +}; + +Ice.BoolHelper = defineBuiltinHelper(ostr.writeBool, istr.readBool, 1, Ice.OptionalFormat.F1); +Ice.LongHelper = defineBuiltinHelper(ostr.writeLong, istr.readLong, 8, Ice.OptionalFormat.F8); +Ice.LongHelper.validate = function(v) +{ + // + // For a long to be valid both words must be within the range of UINT32 + // + return v.low >= MIN_UINT32_VALUE && v.low <= MAX_UINT32_VALUE && + v.high >= MIN_UINT32_VALUE && v.high <= MAX_UINT32_VALUE; +}; + +Ice.StringHelper = defineBuiltinHelper(ostr.writeString, istr.readString, 1, Ice.OptionalFormat.VSize); + +Ice.ObjectHelper = class +{ + static write(os, v) + { + os.writeValue(v); + } + + static read(is) + { + let o; + is.readValue(v => o = v, Ice.Value); + return o; + } + + static writeOptional(os, tag, v) + { + os.writeOptionalValue(tag, Ice.OptionalFormat.Class, ostr.writeValue, v); + } + + static readOptional(is, tag) + { + let o; + is.readOptionalValue(tag, v => o = v, Ice.Value); + return o; + } + + static get minWireSize() + { + return 1; + } +}; + +Ice.InputStream = InputStream; +Ice.OutputStream = OutputStream; +module.exports.Ice = Ice; |