diff options
Diffstat (limited to 'matlab/lib/+IceInternal/EncapsDecoder11.m')
-rw-r--r-- | matlab/lib/+IceInternal/EncapsDecoder11.m | 407 |
1 files changed, 324 insertions, 83 deletions
diff --git a/matlab/lib/+IceInternal/EncapsDecoder11.m b/matlab/lib/+IceInternal/EncapsDecoder11.m index d1ced40e930..aa01b8e5c02 100644 --- a/matlab/lib/+IceInternal/EncapsDecoder11.m +++ b/matlab/lib/+IceInternal/EncapsDecoder11.m @@ -11,10 +11,46 @@ ICE_LICENSE file included in this distribution. classdef EncapsDecoder11 < IceInternal.EncapsDecoder methods - function obj = EncapsDecoder11(is, encaps) - obj = obj@IceInternal.EncapsDecoder(is, encaps); + function obj = EncapsDecoder11(is, encaps, sliceValues, valueFactoryManager, compactIdResolver) + obj = obj@IceInternal.EncapsDecoder(is, encaps, sliceValues, valueFactoryManager); + obj.compactIdResolver = compactIdResolver; + obj.current = []; + obj.valueIdIndex = 1; end + + function readValue(obj, cb) + import IceInternal.Protocol; + index = obj.is.readSize(); + if index < 0 + throw(Ice.MarshalException('', '', 'invalid object id')); + elseif index == 0 + if ~isempty(cb) + cb([]); + end + elseif ~isempty(obj.current) && bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_INDIRECTION_TABLE) + % + % When reading a class 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 ~isempty(cb) + sz = length(obj.current.indirectPatchList); + obj.current.indirectPatchList(sz + 1).index = index - 1; + obj.current.indirectPatchList(sz + 1).cb = cb; + end + else + obj.readInstance(index, cb); + end + end + function throwException(obj) + import IceInternal.Protocol; assert(isempty(obj.current)); obj.push(IceInternal.SliceType.ExceptionSlice); @@ -28,7 +64,7 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder % % Translate the type ID into a class name. % - cls = Ice.Util.idToClass(obj.current.typeId); + cls = IceInternal.Util.idToClass(obj.current.typeId); % % Try to instantiate the class. @@ -54,7 +90,7 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder % % If this is the last slice, raise an exception and stop unmarshaling. % - if bitand(obj.current.sliceFlags, obj.FLAG_IS_LAST_SLICE) + if bitand(obj.current.sliceFlags, Protocol.FLAG_IS_LAST_SLICE) throw(Ice.UnknownUserException('', '', mostDerivedId)); end @@ -62,26 +98,25 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder end end end + function startInstance(obj, sliceType) assert(obj.current.sliceType == sliceType); obj.current.skipFirstSlice = true; end - function endInstance(obj) % TODO: SlicedData - %{ - SlicedDataPtr slicedData; - if(preserve) - { - slicedData = readSlicedData(); - } - _current->slices.clear(); - _current->indirectionTables.clear(); - _current = _current->previous; - return slicedData; - %} - obj.current.slices = []; + + function r = endInstance(obj, preserve) + slicedData = []; + if preserve + slicedData = obj.readSlicedData(); + end + obj.current.slices = {}; + obj.current.indirectionTables = {}; obj.current = obj.current.previous; + r = slicedData; end + function r = startSlice(obj) + import IceInternal.Protocol; % % If first slice, don't read the header, it was already read in % readInstance or throwException to find the factory. @@ -101,11 +136,11 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder % if obj.current.sliceType == IceInternal.SliceType.ValueSlice % Must be checked first! - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_TYPE_ID_COMPACT) == obj.FLAG_HAS_TYPE_ID_COMPACT + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_TYPE_ID_COMPACT) == Protocol.FLAG_HAS_TYPE_ID_COMPACT obj.current.typeId = ''; obj.current.compactId = obj.is.readSize(); - elseif bitand(obj.current.sliceFlags, bitor(obj.FLAG_HAS_TYPE_ID_STRING, obj.FLAG_HAS_TYPE_ID_INDEX)) - obj.current.typeId = obj.readTypeId(bitand(obj.current.sliceFlags, obj.FLAG_HAS_TYPE_ID_INDEX) > 0); + elseif bitand(obj.current.sliceFlags, bitor(Protocol.FLAG_HAS_TYPE_ID_STRING, Protocol.FLAG_HAS_TYPE_ID_INDEX)) + obj.current.typeId = obj.readTypeId(bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_TYPE_ID_INDEX) > 0); obj.current.compactId = -1; else % Only the most derived slice encodes the type ID for the compact format. @@ -119,7 +154,7 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder % % Read the slice size if necessary. % - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_SLICE_SIZE) + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_SLICE_SIZE) obj.current.sliceSize = obj.is.readInt(); if obj.current.sliceSize < 4 throw(Ice.UnmarshalOutOfBoundsException()); @@ -130,60 +165,63 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder r = obj.current.typeId; end + function endSlice(obj) - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_OPTIONAL_MEMBERS) + import IceInternal.Protocol; + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_OPTIONAL_MEMBERS) obj.is.skipOptionals(); end % - % Read the indirect object table if one is present. + % Read the indirection table if one is present and transform the + % indirect patch list into patch entries with direct references. % - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_INDIRECTION_TABLE) - %{ - % TODO - IndexList indirectionTable(obj.is.readAndCheckSeqSize(1)); - for(IndexList::iterator p = indirectionTable.begin(); p != indirectionTable.end(); ++p) - { - *p = readInstance(obj.is.readSize(), 0, 0); - } + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_INDIRECTION_TABLE) + % + % The table is written as a sequence<size> to conserve space. + % + sz = obj.is.readAndCheckSeqSize(1); + indirectionTable = cell(1, sz); + for i = 1:sz + indirectionTable{i} = obj.readInstance(obj.is.readSize(), []); + end % % Sanity checks. If there are optional members, it's possible - % that not all object references were read if they are from + % that not all instance references were read if they are from % unknown optional data members. % - if(indirectionTable.empty()) - { - throw MarshalException(__FILE__, __LINE__, "empty indirection table"); - } - if(obj.current.indirectPatchList.empty() && !(obj.current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS)) - { - throw MarshalException(__FILE__, __LINE__, "no references to indirection table"); - } + if length(indirectionTable) == 0 + throw(Ice.MarshalException('', '', 'empty indirection table')); + end + if isempty(obj.current.indirectPatchList) && ... + bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_OPTIONAL_MEMBERS) == 0 + throw(Ice.MarshalException('', '', 'no references to indirection table')); + end % % Convert indirect references into direct references. % - IndirectPatchList::iterator p; - for(p = obj.current.indirectPatchList.begin(); p != obj.current.indirectPatchList.end(); ++p) - { - assert(p->index >= 0); - if(p->index >= static_cast<Int>(indirectionTable.size())) - { - throw MarshalException(__FILE__, __LINE__, "indirection out of range"); - } - addPatchEntry(indirectionTable[p->index], p->patchFunc, p->patchAddr); - } - obj.current.indirectPatchList.clear(); - %} + if ~isempty(obj.current.indirectPatchList) + for e = obj.current.indirectPatchList + assert(e.index >= 0); + if e.index >= length(indirectionTable) + throw(Ice.MarshalException('', '', 'indirection out of range')); + end + obj.addPatchEntry(indirectionTable{e.index}, e.cb); + end + obj.current.indirectPatchList = []; + end end end + function skipSlice(obj) + import IceInternal.Protocol; %obj.is.traceSkipSlice(obj.current.typeId, obj.current.sliceType); start = obj.is.pos(); - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_SLICE_SIZE) + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_SLICE_SIZE) assert(obj.current.sliceSize >= 4); obj.is.skip(obj.current.sliceSize - 4); else @@ -202,8 +240,8 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder info = Ice.SliceInfo(); info.typeId = obj.current.typeId; info.compactId = obj.current.compactId; - info.hasOptionalMembers = bitand(obj.current.sliceFlags, obj.FLAG_HAS_OPTIONAL_MEMBERS) > 0; - info.isLastSlice = bitand(obj.current.sliceFlags, obj.FLAG_IS_LAST_SLICE) > 0; + info.hasOptionalMembers = bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_OPTIONAL_MEMBERS) > 0; + info.isLastSlice = bitand(obj.current.sliceFlags, Protocol.FLAG_IS_LAST_SLICE) > 0; if info.hasOptionalMembers % % Don't include the optional member end marker. It will be re-written by @@ -214,57 +252,260 @@ classdef EncapsDecoder11 < IceInternal.EncapsDecoder info.bytes = obj.is.getBytes(start, obj.is.pos()); end - %{ - % TODO - obj.current.indirectionTables.push_back(IndexList()); - % - % Read the indirect object table. We read the instances or their + % Read the indirect instance table. We read the instances or their % IDs if the instance is a reference to an already un-marhsaled % object. % % The SliceInfo object sequence is initialized only if % readSlicedData is called. % - if bitand(obj.current.sliceFlags, obj.FLAG_HAS_INDIRECTION_TABLE) - IndexList& table = obj.current.indirectionTables.back(); - table.resize(obj.is.readAndCheckSeqSize(1)); - for(IndexList::iterator p = table.begin(); p != table.end(); ++p) - { - *p = readInstance(obj.is.readSize(), 0, 0); - } - - obj.current.slices.push_back(info); - %} + if bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_INDIRECTION_TABLE) + sz = obj.is.readAndCheckSeqSize(1); + indirectionTable = cell(1, sz); + for i = 1:sz + indirectionTable{i} = obj.readInstance(obj.is.readSize(), []); + end + obj.current.indirectionTables{end + 1} = indirectionTable; + else + obj.current.indirectionTables{end + 1} = {}; + end + + obj.current.slices{end + 1} = info; end + function r = readOptional(obj, readTag, expectedFormat) + import IceInternal.Protocol; if isempty(obj.current) - r = obj.is.readOptImpl(readTag, expectedFormat); + r = obj.is.readOptionalImpl(readTag, expectedFormat); return; - elseif bitand(obj.current.sliceFlags, obj.FLAG_HAS_OPTIONAL_MEMBERS) - r = obj.is.readOptImpl(readTag, expectedFormat); + elseif bitand(obj.current.sliceFlags, Protocol.FLAG_HAS_OPTIONAL_MEMBERS) + r = obj.is.readOptionalImpl(readTag, expectedFormat); return; end r = false; end + end methods(Access=private) + function r = readInstance(obj, index, cb) + import IceInternal.Protocol; + assert(index > 0); + + if index > 1 + if ~isempty(cb) + obj.addPatchEntry(index, cb); + end + r = index; + return; + end + + obj.push(IceInternal.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. + % + obj.valueIdIndex = obj.valueIdIndex + 1; + index = obj.valueIdIndex; + + % + % Read the first slice header. + % + obj.startSlice(); + mostDerivedId = obj.current.typeId; + v = []; + while true + updateCache = false; + + if obj.current.compactId >= 0 + updateCache = true; + + % + % Translate a compact (numeric) type ID into a class. + % + if isempty(obj.compactIdCache) % Lazy initialization. + obj.compactIdCache = containers.Map('KeyType', 'int32', 'ValueType', 'char'); + else + % + % Check the cache to see if we've already translated the compact type ID into a class. + % + if obj.compactIdCache.isKey(obj.current.compactId) + cls = obj.compactIdCache(obj.current.compactId); + try + v = eval(cls); + updateCache = false; + catch e + throw(Ice.NoValueFactoryException('', 'no value factory', ... + sprintf('compact ID %d', obj.current.compactId))); + end + end + end + + % + % If we haven't already cached a class for the compact ID, then try to translate the + % compact ID into a type ID. + % + if isempty(v) + obj.current.typeId = ''; + if ~isempty(obj.compactIdResolver) + try + obj.current.typeId = obj.compactIdResolver(obj.current.compactId); + catch ex + if isa(ex, 'Ice.LocalException') + rethrow(ex); + else + throw(Ice.MarshalException('', '', ... + sprintf('exception in compact ID resolver for ID %d', ... + obj.current.compactId))); + end + end + end + + if isempty(obj.current.typeId) + obj.current.typeId = obj.resolveCompactId(obj.current.compactId); + end + end + end + + if isempty(v) && ~isempty(obj.current.typeId) + v = obj.newInstance(obj.current.typeId); + end + + if ~isempty(v) + if updateCache + assert(obj.current.compactId >= 0); + obj.compactIdCache(obj.current.compactId) = class(v); + end + + % + % We have an instance, get out of this loop. + % + break; + end + + % + % If slicing is disabled, stop unmarshaling. + % + if ~obj.sliceValues + throw(Ice.NoValueFactoryException('', '', 'no value factory found and slicing is disabled', ... + obj.current.typeId)); + end + + % + % Slice off what we don't understand. + % + obj.skipSlice(); + + % + % If this is the last slice, keep the instance as an opaque + % UnknownSlicedValue object. + % + if bitand(obj.current.sliceFlags, Protocol.FLAG_IS_LAST_SLICE) + % + % Provide a factory with an opportunity to supply the instance. + % We pass the "::Ice::Object" ID to indicate that this is the + % last chance to preserve the instance. + % + v = obj.newInstance(Ice.Value.ice_staticId()); + if isempty(v) + v = Ice.UnknownSlicedValue(mostDerivedId); + end + + break; + end + + obj.startSlice(); % Read next Slice header for next iteration. + end + + % + % Unmarshal the instance. + % + obj.unmarshal(index, v); + + if isempty(obj.current) && ~isempty(obj.patchMap) + % + % If any entries remain in the patch map, the sender has sent an index for an instance, but failed + % to supply the instance. + % + throw(Ice.MarshalException('', '', 'index for class received, but no instance')); + end + + if ~isempty(cb) + cb(v); + end + + r = index; + end + + function r = readSlicedData(obj) + if isempty(obj.current.slices) % No preserved slices. + r = []; + end + + % + % The _indirectionTables member holds the indirection table for each slice + % in _slices. + % + assert(length(obj.current.slices) == length(obj.current.indirectionTables)); + function setInstance(si, n, v) + si.instances{n} = v; + end + for n = 1:length(obj.current.slices) + % + % 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. + % + table = obj.current.indirectionTables{n}; + info = obj.current.slices{n}; + info.instances = cell(1, length(table)); + for j = 1:length(info.instances) + obj.addPatchEntry(table{j}, @(v) setInstance(info, j, v)); + end + end + + r = obj.current.slices; % Makes a copy + end + + function r = resolveCompactId(obj, id) + type = ''; + + if ~isempty(obj.compactIdResolver) + try + type = obj.compactIdResolver(id); + catch ex + if isa(ex, 'Ice.LocalException') + rethrow(ex); + else + throw(Ice.MarshalException('', '', sprintf('exception in compact ID resolver for ID %d', id))); + end + end + end + + if isempty(type) + prop = sprintf('IceCompactId.TypeId_%d.typeId', id); + try + type = eval(prop); + catch ex + end + end + + r = type; + end + function push(obj, sliceType) - obj.current = IceInternal.InstanceData(obj.current); + obj.current = IceInternal.EncapsDecoder11_InstanceData(obj.current); obj.current.sliceType = sliceType; obj.current.skipFirstSlice = false; end end properties(Access=private) + compactIdResolver current - end - properties(Constant,Access=private) - FLAG_HAS_TYPE_ID_STRING = bitshift(1, 0) - FLAG_HAS_TYPE_ID_INDEX = bitshift(1, 1) - FLAG_HAS_TYPE_ID_COMPACT = bitor(bitshift(1, 0), bitshift(1, 1)) - FLAG_HAS_OPTIONAL_MEMBERS = bitshift(1, 2) - FLAG_HAS_INDIRECTION_TABLE = bitshift(1, 3) - FLAG_HAS_SLICE_SIZE = bitshift(1, 4) - FLAG_IS_LAST_SLICE = bitshift(1, 5) + valueIdIndex + compactIdCache end end |