File: ExtensionRegistry.java

package info (click to toggle)
ruby-msgpack 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 972 kB
  • sloc: ruby: 4,789; ansic: 4,309; java: 1,809; makefile: 4
file content (167 lines) | stat: -rw-r--r-- 5,350 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package org.msgpack.jruby;

import org.jruby.Ruby;
import org.jruby.RubyHash;
import org.jruby.RubyArray;
import org.jruby.RubyModule;
import org.jruby.RubyFixnum;
import org.jruby.RubySymbol;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.Map;
import java.util.HashMap;

public class ExtensionRegistry {
  private final Map<RubyModule, ExtensionEntry> extensionsByModule;
  private final Map<RubyModule, ExtensionEntry> extensionsByAncestor;
  private final ExtensionEntry[] extensionsByTypeId;

  public ExtensionRegistry() {
    this(new HashMap<RubyModule, ExtensionEntry>(), new ExtensionEntry[256]);
  }

  private ExtensionRegistry(Map<RubyModule, ExtensionEntry> extensionsByModule, ExtensionEntry[] extensionsByTypeId) {
    this.extensionsByModule = new HashMap<RubyModule, ExtensionEntry>(extensionsByModule);
    this.extensionsByAncestor = new HashMap<RubyModule, ExtensionEntry>();
    this.extensionsByTypeId = extensionsByTypeId.clone();
  }

  public ExtensionRegistry dup() {
    return new ExtensionRegistry(extensionsByModule, extensionsByTypeId);
  }

  public IRubyObject toInternalPackerRegistry(ThreadContext ctx) {
    RubyHash hash = RubyHash.newHash(ctx.runtime);
    for (RubyModule extensionModule : extensionsByModule.keySet()) {
      ExtensionEntry entry = extensionsByModule.get(extensionModule);
      if (entry.hasPacker()) {
        hash.put(extensionModule, entry.toPackerTuple(ctx));
      }
    }
    return hash;
  }

  public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) {
    RubyHash hash = RubyHash.newHash(ctx.runtime);
    for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) {
      ExtensionEntry entry = extensionsByTypeId[typeIdIndex];
      if (entry != null && entry.hasUnpacker()) {
        IRubyObject typeId = RubyFixnum.newFixnum(ctx.runtime, typeIdIndex - 128);
        hash.put(typeId, entry.toUnpackerTuple(ctx));
      }
    }
    return hash;
  }

  public void put(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject unpackerProc) {
    ExtensionEntry entry = new ExtensionEntry(mod, typeId, recursive, packerProc, unpackerProc);
    extensionsByModule.put(mod, entry);
    extensionsByTypeId[typeId + 128] = entry;
    extensionsByAncestor.clear();
  }

  public ExtensionEntry lookupExtensionByTypeId(int typeId) {
    ExtensionEntry e = extensionsByTypeId[typeId + 128];
    if (e != null && e.hasUnpacker()) {
      return e;
    }
    return null;
  }

  public ExtensionEntry lookupExtensionForObject(IRubyObject object) {
    RubyModule lookupClass = null;
    ExtensionEntry entry = null;
    /*
     * Objects of type Integer (Fixnum, Bignum), Float, Symbol and frozen
     * String have no singleton class and raise a TypeError when trying to get
     * it.
     */
    lookupClass = object.getMetaClass();
    entry = extensionsByModule.get(lookupClass);
    if (entry != null && entry.hasPacker()) {
      return entry;
    }

    RubyModule realClass = object.getType();
    if (realClass != lookupClass) {
      entry = extensionsByModule.get(realClass);
      if (entry != null && entry.hasPacker()) {
        return entry;
      }
    }

    entry = findEntryByModuleOrAncestor(lookupClass);
    if (entry != null && entry.hasPacker()) {
      return entry;
    }
    return null;
  }

  private ExtensionEntry findEntryByModuleOrAncestor(final RubyModule mod) {
    ThreadContext ctx = mod.getRuntime().getCurrentContext();
    for (RubyModule extensionModule : extensionsByModule.keySet()) {
      RubyArray<?> ancestors = (RubyArray)mod.callMethod(ctx, "ancestors");
      if (ancestors.callMethod(ctx, "include?", extensionModule).isTrue()) {
        return extensionsByModule.get(extensionModule);
      }
    }
    return null;
  }

  public static class ExtensionEntry {
    private final RubyModule mod;
    private final int typeId;
    private final boolean recursive;
    private final IRubyObject packerProc;
    private final IRubyObject unpackerProc;

    public ExtensionEntry(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject unpackerProc) {
      this.mod = mod;
      this.typeId = typeId;
      this.recursive = recursive;
      this.packerProc = packerProc;
      this.unpackerProc = unpackerProc;
    }

    public RubyModule getExtensionModule() {
      return mod;
    }

    public int getTypeId() {
      return typeId;
    }

    public boolean isRecursive() {
      return recursive;
    }

    public boolean hasPacker() {
      return packerProc != null && !packerProc.isNil();
    }

    public boolean hasUnpacker() {
      return unpackerProc != null && !unpackerProc.isNil();
    }

    public IRubyObject getPackerProc() {
      return packerProc;
    }

    public IRubyObject getUnpackerProc() {
      return unpackerProc;
    }

    public RubyArray<?> toPackerTuple(ThreadContext ctx) {
      return ctx.runtime.newArray(new IRubyObject[] {ctx.runtime.newFixnum(typeId), packerProc});
    }

    public RubyArray<?> toUnpackerTuple(ThreadContext ctx) {
      return ctx.runtime.newArray(new IRubyObject[] {mod, unpackerProc});
    }

    public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) {
      return new IRubyObject[] {packerProc, ctx.runtime.newFixnum(typeId)};
    }
  }
}